Как мне проанализировать JSON, у которого экранированы акценты ключей и значений, не влияя на экранирование в значениях полей?

1

Запрос веб-узлов с 155-questions-active Я получаю следующий (malformatted) JSON:

{
    "action":"155-questions-active",
    "data":
        "{
            \"siteBaseHostAddress\":\"stackoverflow.com\",
            \"id\":23747905,
            \"titleEncodedFancy\":\"Load sqlite extension in Django\",
            \"bodySummary\":\"I have built a sqlite <snip>\",
            \"tags\":[\"django\",\"sqlite\",\"pysqlite\"],
            \"lastActivityDate\":1400544795,
            \"url\":\"http://stackoverflow.com/questions/23747905/<snip>\",
            \"ownerUrl\":\"http://stackoverflow.com/users/1311165/pro-chats\",
            \"ownerDisplayName\":\"Pro Chats\",
            \"apiSiteParameter\":\"stackoverflow\"
        }"
}

После применения некоторых исправлений

    private string MakeJsonCapable(string input)
    {
        input = input.Trim();
        input = input.Replace("data\":\"", "data\":");
        input = input.Remove(input.LastIndexOf("\""), 1);
        input = input.Replace("\\", string.Empty);

        return input;
    }

Я получаю этот результат:

{
  "action": "155-questions-active",
  "data": {
    "siteBaseHostAddress": "stackoverflow.com",
    "id": 23747905,
    "titleEncodedFancy": "Load sqlite extension in Django",
    "bodySummary": "I have built a sqlite <snip>",
    "tags": [
      "django",
      "sqlite",
      "pysqlite"
    ],
    "lastActivityDate": 1400544795,
    "url": "http:\/\/stackoverflow.com\/questions\/23747905\/<snip>",
    "ownerUrl": "http:\/\/stackoverflow.com\/users\/1311165\/pro-chats",
    "ownerDisplayName": "Pro Chats",
    "apiSiteParameter": "stackoverflow"
  }
}

Какой теперь приемлемый JSON (я использую инструмент онлайн-формата JSON для проверки этого), который отлично разбирается JSON.NET.

Проблема возникает, когда значение (до сих пор я видел его только в bodySummary но я подозреваю, что titleEncodedFancy также, вероятно, имеет это), содержит ". Литеральное значение, которое передается перед тем, как сделать его Json-able - \\\"Compliant Solution\\\": 3 обратных слэша и акцент.

Обратите внимание, что это буквальное значение и не включает в себя обратную косую черту от отладчика: это берется непосредственно из textview; переменная часов показывает 7 обратных косых черт.

Очевидно, что это проблема, потому что теперь мой bodySummary содержит bodySummary " который повредит десериализацию. По этой причине я не могу создать пользовательский JsonConverter чтобы сбежать от них сам либо потому, что он не получит правильные значения в первую очередь.

Как удалить нежелательные обратные косые черты, которые появляются перед акцентами, которые означают начало и конец имени поля и его значения?

В качестве альтернативы: возможно, я неправильно разбираю поле data. Если это так: каков правильный путь?

Теги:
json.net

2 ответа

2
Лучший ответ

Здесь у вас есть данные, которые были сериализованы для строки, помещены внутри другого объекта и затем сериализованы во второй раз. Чтобы все получилось правильно, вы можете изменить процесс. Определите два класса: один для внешней сериализации и один для внутреннего:

class Outer
{
    public string Action { get; set; }
    public string Data { get; set; }
}

class Inner
{
    public string SiteBaseHostAddress { get; set; }
    public int Id { get; set; }
    public string TitleEncodedFancy { get; set; }
    public string BodySummary { get; set; }
    public string[] Tags { get; set; }
    public int LastActivityDate { get; set; }
    public string Url { get; set; }
    public string OwnerUrl { get; set; }
    public string OwnerDisplayName { get; set; }
    public string ApiSiteParameter { get; set; }
}

Затем десериализуйте следующим образом:

Outer outer = JsonConvert.DeserializeObject<Outer>(json);
Inner inner = JsonConvert.DeserializeObject<Inner>(outer.Data);

Когда вы это сделаете, НЕ применяйте "исправления" к входной строке. Пусть парсер JSON выполняет свою работу.

РЕДАКТИРОВАТЬ

Если вы хотите сохранить отношения родитель-потомок, вам понадобится настраиваемый JsonConverter для обработки десериализации дочернего объекта. Для этого вам сначала нужно изменить определение внешнего класса на это:

class Outer
{
    public string Action { get; set; }
    [JsonConverter(typeof(InnerConverter))]
    public Inner Data { get; set; }
}

Создайте класс InnerConverter следующим образом:

class InnerConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Inner));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        return JsonConvert.DeserializeObject<Inner>(token.ToString());
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

И, наконец, вы можете десериализовать вот так:

Outer outer = JsonConvert.DeserializeObject<Outer>(json);
  • 0
    Есть ли польза от этого, вместо того, чтобы сохранять структуру родитель-потомок?
  • 0
    Только то, что это работает с минимумом суеты. Можно сохранить структуру parent-child, но для этого вам понадобится немного больше кода. Если вы не возражаете против того, что у дочернего элемента есть имя свойства, отличное от Data , вы можете просто добавить дополнительное свойство к родительскому объекту для хранения десериализованного дочернего экземпляра, а затем добавить дочерний элемент к родительскому объекту после десериализации. Если вам нужно, чтобы дочернее свойство называлось Data и фактически содержало экземпляр, а не строку, то вам, вероятно, понадобится конвертер, чтобы иметь возможность десериализовать родительский объект.
Показать ещё 6 комментариев
1

Следуя предложению Брайана Роджерса, я создал простой конвертер, который обрабатывает все это для меня:

отклик

public sealed class Response
{
    [JsonProperty("action")]
    public string Action { get; internal set; }

    [JsonProperty("data")]
    [JsonConverter(typeof (DataConverter))]
    public Data Data { get; internal set; }
}

Данные

public sealed class Data
{
    [JsonProperty("siteBaseHostAddress")]
    public string SiteBaseHostAddress { get; internal set; }

    [JsonProperty("id")]
    public string Id { get; internal set; }

    [JsonProperty("titleEncodedFancy")]
    public string TitleEncodedFancy { get; internal set; }

    [JsonProperty("bodySummary")]
    public string BodySummary { get; internal set; }

    [JsonProperty("tags")]
    public IEnumerable<string> Tags { get; internal set; }

    [JsonProperty("lastActivityDate")]
    [JsonConverter(typeof (EpochTimeConverter))]
    public DateTime LastActivityDate { get; internal set; }

    [JsonProperty("url")]
    [JsonConverter(typeof (UriConverter))]
    public Uri QuestionUrl { get; internal set; }

    [JsonProperty("ownerUrl")]
    [JsonConverter(typeof (UriConverter))]
    public Uri OwnerUrl { get; internal set; }

    [JsonProperty("ownerDisplayName")]
    public string OwnerDisplayName { get; internal set; }

    [JsonProperty("apiSiteParameter")]
    public string ApiSiteParameter { get; internal set; }
}

DataConverter

internal sealed class DataConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof (string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var value = reader.Value as string;
        return JsonConvert.DeserializeObject<Data>(value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Теперь я могу полностью десериализовать его

var responseObject = JsonConvert.DeserializeObject<Response>(result);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню