如何反序列化狡猾的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. WTF甚至是什么?如果我放弃'颜色',那么我可以得到一串字符串,但我无法调整以开箱即用。

  2. 这是一个没有方括号的数组。我可以通过将其包裹在其中来解决这个问题。但是有没有办法支持开箱即用?

  3. 房产没有引号。反序列化对这些很不错..但是反序列化只是没有骰子。

任何处理这个结构和内部结构的建议吗?

+1

你不能使用标准的JSON解析器来解析它,因为它不是有效的JSON。你必须编写你自己的,或找到一种方法来处理传入的字符串,然后再将它交给解析器。显然,最好的解决方案是修复这个狡猾的数据源,但我们是否理解这超出了你的控制范围?究竟是什么抽出这个垃圾,并调用JSON?大多数编程语言现在都有内置的JSON序列化工具,所以实际上很难产生无效的输出。 – ADyson

+0

您一次提出多个问题。首选格式是[每个帖子一个问题](https://meta.stackexchange.com/q/222735/344280)。 – dbc

回答你的问题#1 - #3依次是:

  1. Json.NET不支持的形式colors["Open"](其中,因为你注意到,违反了JSON standard)读取狡猾的属性值。

    相反,您需要手动修复这些值,例如,通过某种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 JsonConverter内拨打JsonWriter.WriteRawValue()

    定义以下转换器:在您的JSON

    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; } 
    } 
    

    然后,重新序列化时,你会得到原来狡猾的值:

    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如下。

  2. 支持反向扩展逗号分隔的无外部括号的JSON将在10.0.3之后的Json.NET的下一个版本中发布。有关详细信息,请参阅Issue 1396Issue 1355。您需要设置JsonTextReader.SupportMultipleContent = true才能使其工作。

    同时,作为一种解决方法,你可以通过Rex MChainedTextReaderpublic static TextReader Extensions.Concat(this TextReader first, TextReader second)从答案How to string multiple TextReaders together?并用方括号括[]您的JSON。

    因此,你会反序列化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分别序列化每个项目,则可以重新序列化无根外括号的根集合。您也可以在每个序列化商品之间手动编写一个,,并在每个JsonTextWriter共享的基础TextWriter之间写入。

  3. 如果您设置了JsonTextWriter.QuoteName = false,则可以序列化不带周围引号字符的JSON属性名称。

    因此,重新系列化你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 fiddle显示操作这一切。