Запрос веб-узлов с 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
. Если это так: каков правильный путь?
Здесь у вас есть данные, которые были сериализованы для строки, помещены внутри другого объекта и затем сериализованы во второй раз. Чтобы все получилось правильно, вы можете изменить процесс. Определите два класса: один для внешней сериализации и один для внутреннего:
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);
Следуя предложению Брайана Роджерса, я создал простой конвертер, который обрабатывает все это для меня:
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; }
}
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);
Data
, вы можете просто добавить дополнительное свойство к родительскому объекту для хранения десериализованного дочернего экземпляра, а затем добавить дочерний элемент к родительскому объекту после десериализации. Если вам нужно, чтобы дочернее свойство называлосьData
и фактически содержало экземпляр, а не строку, то вам, вероятно, понадобится конвертер, чтобы иметь возможность десериализовать родительский объект.