【问题标题】:How to deserialize JSON which can be an array or a single object如何反序列化可以是数组或单个对象的 JSON
【发布时间】:2015-02-08 15:26:26
【问题描述】:

我对使用 JSON.net 还很陌生,并且遇到了一些我得到的 json 问题,这些 json 有时以数组形式出现,有时以单个对象形式出现。这是我在 json 中看到的示例

它进来的一种方式......

{
  "Make": "Dodge",
  "Model": "Charger",
  "Lines": [
    {
      "line": "base",
      "engine": "v6",
      "color": "red"
    },
    {
      "line": "R/T",
      "engine": "v8",
      "color": "black"
    }
  ],
  "Year": "2013"
}

它可以通过另一种方式进入

{
  "Make": "Dodge",
  "Model": "Charger",
  "Lines": {
    "line": "base",
    "engine": "v6",
    "color": "red"
  },
  "Year": "2013"
}

这是我一直在使用的代码,它在第一种方式下工作并在第二种情况下引发异常。一直在网上搜索实现这一点的方法,但我真的被困住了。

Public Class jsonCar
    Public Property make As String
    Public Property model As String
    Public Property lines As List(Of jsonCarLines)
    Public Property year As String
End Class

Public Class jsonCarLines
    Public Property line As String
    Public Property engine As String
    Public Property color As String
End Class

Module Module1
    Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": [{""line"":""base"",""engine"": ""v6"",""color"":""red""},{""line"":""R/T"",""engine"":""v8"",""color"":""black""}],""Year"":""2013""}"
    'Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": {""line"":""R/T"",""engine"":""v8"",""color"":""black""},""Year"":""2013""}"
    Sub Main()
        Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)

        Console.WriteLine("Make: " & car.make)
        Console.WriteLine("Model: " & car.model)
        Console.WriteLine("Year: " & car.year)
        Console.WriteLine("Lines: ")
        For Each ln As jsonCarLines In car.lines
            Console.WriteLine("    Name: " & ln.line)
            Console.WriteLine("    Engine: " & ln.engine)
            Console.WriteLine("    Color: " & ln.color)
            Console.WriteLine()
        Next
        Console.ReadLine()
    End Sub

End Module

我猜这可能需要一个自定义的 JsonConverter,但我不知道如何设置它。

【问题讨论】:

  • 我不认为单个对象可以作为具有一个对象的数组写入 JSON?这将大大简化事情。事实上,如果lines 被允许为一个或多个,这可能是正确的方法。否则,您将不得不编写自定义解析器或预处理器来添加必要的[] 字符。为什么允许这种设计?
  • 不幸的是,我无法控制传入的 json 字符串。没有进行字符串操作,我只能坚持我所得到的。我查看了布赖恩提到的问题,但在让它发挥作用时遇到了问题。我认为这里的解决方案在原生 VB 中感觉更直接,这对我来说是额外的 :)
  • @user4543329 由于您无法从重复的问题中获得解决方案,因此我添加了一个答案,说明如何在您的特定情况下使用SingleOrArrayConverter(Of T)

标签: json vb.net json.net


【解决方案1】:

以下是如何让链接的duplicate question 中的SingleOrArrayConverter 解决方案适用于您的用例。

首先,这是 VB 翻译的转换器代码。把它保存到项目中某处的类文件中。然后,您可以轻松地将其重用于未来的任何此类情况。

Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Public Class SingleOrArrayConverter(Of T)
    Inherits JsonConverter

    Public Overrides Function CanConvert(objectType As Type) As Boolean
        Return objectType = GetType(List(Of T))
    End Function

    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
        Dim token As JToken = JToken.Load(reader)

        If (token.Type = JTokenType.Array) Then
            Return token.ToObject(Of List(Of T))()
        End If

        Return New List(Of T) From {token.ToObject(Of T)()}
    End Function

    Public Overrides ReadOnly Property CanWrite As Boolean
        Get
            Return False
        End Get
    End Property

    Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
        Throw New NotImplementedException
    End Sub

End Class

现在你有了这个转换器,任何时候你有一个可以是列表或单个项目的属性,你所要做的就是在你的类中将它声明为一个列表,然后用 @987654325 注释该列表@ 属性,使其使用 SingleOrArrayConverter 类。在你的情况下,这看起来像这样:

Public Class jsonCar
    Public Property make As String
    Public Property model As String
    <JsonConverter(GetType(SingleOrArrayConverter(Of jsonCarLines)))>
    Public Property lines As List(Of jsonCarLines)
    Public Property year As String
End Class

然后,像往常一样反序列化,它就会按预期工作。

Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)

这是一个完整的演示:https://dotnetfiddle.net/msYNeQ

【讨论】:

    【解决方案2】:

    您可以通过修改您的jsonCar 类来实现这一点,如下所示

    Public Class jsonCar
        Public Property make As String 
        Public Property model As String
        Public Property linesCollection As List(Of jsonCarLines) // Change name
        Public Property lines As String // Change the type to string
        Public Property year As String
    End Class
    

    代码如下:

    Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)
    
    If (car.lines.StartsWith("[")) Then
        car.linesCollection = JsonConvert.DeserializeObject(List(Of jsonCarLines))(car.lines)
    Else
        car.linesCollection = new List(Of jsonCarLines)
        car.linesCollection.Add(JsonConvert.DeserializeObject(Of jsonCarLines)(car.lines))
    EndIf
    

    【讨论】:

    • lines 永远不是字符串。它是一个数组或一个对象。因此,如果我将其更改为字符串,我会在任一版本的 json 中得到一个无效的令牌异常。
    【解决方案3】:

    感谢 crowcoder 和 Kundan。我将这两种方法结合起来,并提出了适用于两种 json 输入的方法。这是最终的代码。

    Public Class jsonCar
        Public Property make As String
        Public Property model As String
        Public Property linesArray As List(Of jsonCarLines)
        Public Property year As String
    End Class
    
    Public Class jsonCarLines
        Public Property line As String
        Public Property engine As String
        Public Property color As String
    End Class
    
    Module Module1
        'Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": [{""line"":""base"",""engine"": ""v6"",""color"":""red""},{""line"":""R/T"",""engine"":""v8"",""color"":""black""}],""Year"":""2013""}"
        Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": {""line"":""R/T"",""engine"":""v8"",""color"":""black""},""Year"":""2013""}"
        Sub Main()
    
            Dim obj As JObject = JsonConvert.DeserializeObject(json)
            Dim ln As JToken = obj("Lines")
    
            Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)
    
            If (ln.GetType() Is GetType(Newtonsoft.Json.Linq.JArray)) Then
                car.linesArray = JsonConvert.DeserializeObject(Of List(Of jsonCarLines))(JsonConvert.SerializeObject(ln))
            End If
    
            If (ln.GetType() Is GetType(Newtonsoft.Json.Linq.JObject)) Then
                car.linesArray = New List(Of jsonCarLines)
                car.linesArray.Add(JsonConvert.DeserializeObject(Of jsonCarLines)(JsonConvert.SerializeObject(ln)))
            End If
    
            Console.WriteLine("Make: " & car.make)
            Console.WriteLine("Model: " & car.model)
            Console.WriteLine("Year: " & car.year)
            Console.WriteLine("Lines: ")
            For Each line As jsonCarLines In car.linesArray
                Console.WriteLine("    Name: " & line.line)
                Console.WriteLine("    Engine: " & line.engine)
                Console.WriteLine("    Color: " & line.color)
                Console.WriteLine()
            Next
            Console.ReadLine()
        End Sub
    
    End Module
    

    非常感谢您的快速回复。这解决了我一直在断断续续地试图弄清楚的问题。

    【讨论】:

      【解决方案4】:

      您通常可以反序列化为 Object,然后检查您拥有的内容。您可以检查 JArray 或 JObject 并采取相应措施。您甚至不需要反序列化为特定类型,您可以使用 Dynamic 对象,但这可能不是最好的主意。

          Module Module1
      
          Sub Main()
           Dim jsonWithArray As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": [{""line"":""base"",""engine"": ""v6"",""color"":""red""},{""line"":""R/T"",""engine"":""v8"",""color"":""black""}],""Year"":""2013""}"
           Dim jsonWithObject As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": {""line"":""base"",""engine"": ""v6"",""color"":""red""},""Year"":""2013""}"
      
           Dim witharray As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonWithArray)
           Dim withstring As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonWithObject)
      
           Dim jtokArray As Newtonsoft.Json.Linq.JToken = witharray("Lines")
           Dim jtokStr As Newtonsoft.Json.Linq.JToken = withstring("Lines")
      
           If (jtokArray.GetType() Is GetType(Newtonsoft.Json.Linq.JArray)) Then
              Console.WriteLine("its an array")
           End If
      
           If (jtokStr.GetType() Is GetType(Newtonsoft.Json.Linq.JObject)) Then
              Console.WriteLine("its an object")
           End If
      
          Console.ReadKey()
      
          End Sub
      
      End Module
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-04
        • 2014-10-24
        相关资源
        最近更新 更多