一尘不染

如何反序列化狡猾的JSON(带有不正确的引号字符串和缺少的括号)?

json

我必须解析(并最终重新序列化)一些狡猾的JSON。它看起来像这样:

{
  name: "xyz",
  id: "29573f59-85fb-4d06-9905-01a3acb2cdbd",
  status: "astatus",
  color: colors["Open"]
},
{
  name: "abc",
  id: "29573f59-85fb-4d06-9905-01a3acb2cdbd",
  status: "astatus",
  color: colors["Open"]
}

这里有很多问题-从最严重的问题开始。

  1. color: colors["Open"]

WTF甚至是吗?如果我放弃“颜色”,那么我可以得到一系列的字符串,但是我不能调整以开箱即用。

  1. 它是一个没有方括号的数组。我可以将它们包裹起来以解决此问题。但是有没有一种现成的支持方式?

  2. 属性没有引号。反序列化对于这些很好,但是重新序列化不是一个骰子。

关于处理此结构的任何建议?


阅读 192

收藏
2020-07-27

共1个答案

一尘不染

按顺序回答您的问题#1-#3:

  1. Json.NET不支持以以下形式读取狡猾的属性值colors["Open"](如您所正确指出的那样,它违反了JSON标准)。

相反,您将需要手动修复这些值,例如通过某种方式Regex

    var regex = new Regex(@"(colors\[)(.*)(\])");
var fixedJsonString = regex.Replace(jsonString, 
    m => string.Format(@"""{0}{1}{2}""", m.Groups[1].Value, m.Groups[2].Value.Replace("\"", "\\\""), m.Groups[3].Value));

这会将color属性值更改为正确转义的JSON字符串:

    color: "colors[\"Open\"]"

但是,Json.NET确实具有通过在custom内调用来
编写
狡猾的属性值的功能。JsonWriter.WriteRawValue()JsonConverter

定义以下转换器:

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

    public override bool CanRead { get { return false; } }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var s = (string)value;
        writer.WriteRawValue(s);
    }
}

然后定义RootObject如下:

    public class RootObject
{
    public string name { get; set; }
    public string id { get; set; }
    public string status { get; set; }

    [JsonConverter(typeof(RawStringConverter))]
    public string color { get; set; }
}

然后,当重新序列化时,您将在JSON中获得原始的躲避值。

  1. 10.0.3之后的下一个Json.NET版本中,将支持反序列化以逗号分隔的JSON,而无需使用方括号。有关详细信息,请参见问题1396问题1355。您需要进行设置JsonTextReader.SupportMultipleContent = true以使其起作用。

因此,您可以按以下方式反序列化JSON:

    List<RootObject> list;
using (var reader = new StringReader("[").Concat(new StringReader(fixedJsonString)).Concat(new StringReader("]")))
using (var jsonReader = new JsonTextReader(reader))
{
    list = JsonSerializer.CreateDefault().Deserialize<List<RootObject>>(jsonReader);
}

(或者,您可以使用[和手动将JSON字符串括起来],但我更喜欢不涉及复制可能很大的字符串的解决方案。)

重新序列根集合,而不外括号,如果你序列化的每个项目单独利用自身可能JsonTextWriterCloseOutput = false。您还可以,在每个序列化项目之间手动将a写入每个用户TextWriter共享的基础JsonTextWriter

  1. 如果设置,则可以序列化JSON属性名称而无需使用引号引起来JsonTextWriter.QuoteName = false

因此,要重新序列化List<RootObject>不带引号的属性名称或大括号,请执行以下操作:

    var sb = new StringBuilder();
bool first = true;
using (var textWriter = new StringWriter(sb))
{
    foreach (var item in list)
    {
        if (!first)
        {
            textWriter.WriteLine(",");
        }
        first = false;
        using (var jsonWriter = new JsonTextWriter(textWriter) { QuoteName = false, Formatting = Formatting.Indented, CloseOutput = false })
        {
            JsonSerializer.CreateDefault().Serialize(jsonWriter, item);
        }
    }
}

var reserializedJson = sb.ToString();

样本.Net小提琴展示了所有这些动作。

2020-07-27