【问题标题】:How to parse deserialized JSON object back into arrays?如何将反序列化的 JSON 对象解析回数组?
【发布时间】:2021-05-30 20:30:31
【问题描述】:

我已经成功地序列化了一整类属性(数组、字符串向量、整数、双精度)。反序列化也可以,但我需要一种方法将返回的对象解析成各种数组,然后将它们设置为等于四个相同维度的数组。

以前,当我使用 BinaryFormatter 时,我可以简单地声明 mydeser As Object,反序列化为 mydeser,然后使用例如直接提取数组。 readarray = mydeser.array.

我确实注意到 JObject 是一种字典,但我不知道键是什么或值是什么,它们是某种 jsonToken。

我尝试使用:

For each kvp as KeyValuePair(String, jsonToken) in myser
Next

但是抛出了异常。那么有没有办法使用一个键,然后将 JObject 的值直接放入数组中,使用 readarray = mydeser("array")——假设 mydeser 是一个字典?

Imports System.IO
Imports Newtonsoft.Json

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim ser As New testSer
    End Sub
End Class

Public Class testSer
    Public Property array As Double(,)
    Public Property vector As Double()
    Public Property strVec As String()
    Public Property IntVec As Integer()
    Sub New()
        serdeser()
    End Sub
    Sub serdeser()
        Dim r As New Random
        ReDim array(1000, 1000)
        ReDim vector(1000)
        ReDim strVec(1000)
        ReDim IntVec(1000)
        For i = 1 To 1000
            vector(i) = r.NextDouble
            strVec(i) = "A"
            IntVec(i) = r.Next(1, 100)
            For j = 1 To 1000
                array(i, j) = r.NextDouble
            Next
        Next

        Dim jsonSerializer As New JsonSerializer

        Dim stream As FileStream = File.Create("D:\test")
        Dim writer As New StreamWriter(stream)
        Dim jsonWriter As New JsonTextWriter(writer)
        jsonSerializer.Serialize(jsonWriter, Me)
        writer.Close()

        Dim stream1 As FileStream = File.Open("D:\test", FileMode.Open, FileAccess.Read, FileShare.Read)
        Dim reader As New StreamReader(stream1)
        Dim jsonTextReader As New JsonTextReader(reader)
        Dim mydeser As Linq.JObject
        mydeser = jsonSerializer.Deserialize(jsonTextReader)

        Dim readarray(1000, 1000) As Double
        Dim readvector(1000) As Double
        Dim readstrVec(1000) As String
        Dim readIntVec(1000) As Integer

        Dim mystr = DirectCast(mydeser.First, Linq.JProperty)
        'readarray = mydeser.array <--need to assign "array" back into readarray
        'readvector = mydeser.vector
        'readstrVec = mydeser.strvec
        'readarray = mydeser.intvec

        reader.Close()
    End Sub

End Class

【问题讨论】:

    标签: json vb.net winforms dictionary json.net


    【解决方案1】:

    一些建议,考虑到这是一个测试类:

    • 您的序列化过程基本正确。最好使用 Using 语句声明 Stream 对象,因此在出现异常时,这些对象会被隐式处理。

    • 如果您只指定要反序列化的类型,反序列化将变得非常简单。类型由您的类的类型表示。你可以写(见示例代码):

      Dim myObject = [JsonSerializer].Deserialize(Of [MyObjectType])([JsonTextReader])
      
    • 您不需要 FileStream,StreamWriter / StreamReader 已经在自己内部使用了 FileStream。如果文件不存在,StreamWriter 会创建该文件。

    • StreamReader 使用Encoding.UTF8 显式初始化:它是默认值,只是使用的编码和文件在没有 BOM 签名的情况下保存的余数。

    • 在退出 Using 块之前,在 JsonTextWriter 上调用 Close()。行为由AutoCompleteOnClose属性决定,默认设置为True,更安全。

    • 反序列化时,将JsonTextReader的FloatParseHandling属性设置为FloatParseHandling.DoubleFloatParseHandling.DecimalDouble 实际上是默认值,请记住这一点,以防您需要处理 Decimal 值,否则将反序列化为 Double

    • 数组的下界是0,而不是1

    • 使您的 Random 对象成为静态字段。这确保了更好的功能(或者,如果需要,它可以让类正常运行)。

    在修改后的示例类中,我重载了构造函数:传递True,将调用用随机数据填充类对象的Build() 方法。
    传递False,生成一个空对象。

    不需要复制反序列化的值,你可以直接从 JsonSerializer 生成一个新的类对象。例如:

    Dim jsonPath = "d:\test.json"
    
    ' Passing True to the Constuctor, calls the Build() method
    Dim serTest = New SerializationTest(True)
    ' Serialize all property values to the specified File
    serTest.Serialize(jsonPath)
    
    ' Creates a new SerializationTest objects and fills it
    ' deserializing the JSON previously saved calling Serialize() 
    Dim serTest2 = New SerializationTest(False).Deserialize(jsonPath)
    

    您可以比较两个对象,serTestserTest2
    如果需要副本,请参阅底部。

    修改类

    Public Class SerializationTest
        Private Shared rnd As New Random()
    
        Public Sub New()
            Me.New(False)
        End Sub
    
        Public Sub New(useBuilder As Boolean)
            If useBuilder Then Build()
        End Sub
    
        Public Property DblArray As Double(,)
        Public Property DblVector As Double()
        Public Property StringVector As String()
        Public Property IntVector As Integer()
    
        Public Sub Serialize(jsonPath As String)
            Using stream As New StreamWriter(jsonPath)
                Dim jWriter As New JsonTextWriter(stream)
                Dim serializer As New JsonSerializer()
                serializer.Serialize(jWriter, Me)
                jWriter.Close()
            End Using
        End Sub
    
        Public Function Deserialize(jsonPath As String) As SerializationTest
            Using reader As New StreamReader(jsonPath, Encoding.UTF8)
                Dim jReader As New JsonTextReader(reader) With {
                    .FloatParseHandling = FloatParseHandling.Double
                }
                Dim serializer As New JsonSerializer()
                Dim deserialized = serializer.Deserialize(Of SerializationTest)(jReader)
                jReader.Close()
                Return deserialized
            End Using
        End Function
    
        Private Sub Build()
            ReDim DblArray(1000, 1000)
            ReDim DblVector(1000)
            ReDim StringVector(1000)
            ReDim IntVector(1000)
            For i = 0 To 1000
                DblVector(i) = rnd.NextDouble()
                StringVector(i) = $"A{i}"
                IntVector(i) = rnd.Next(1, 101)
                For j = 0 To 1000
                    DblArray(i, j) = rnd.NextDouble()
                Next
            Next
        End Sub
    End Class
    

    如果您出于某种原因想要将反序列化的值复制到已初始化但为空的类对象,则将方法更改如下:

    Public Sub Deserialize(jsonPath As String)
        Using reader As New StreamReader(jsonPath, Encoding.UTF8)
            Dim jReader As New JsonTextReader(reader) With {
                .FloatParseHandling = FloatParseHandling.Double
            }
            Dim serializer As New JsonSerializer()
            Dim deserialized = serializer.Deserialize(Of SerializationTest)(jReader)
            jReader.Close()
    
            Me.DblArray = deserialized.DblArray
            Me.DblVector = deserialized.DblVector
            Me.StringVector = deserialized.StringVector
            Me.IntVector = deserialized.IntVector
            deserialized = Nothing
        End Using
    End Sub
    

    并更改填充它的代码:

    Dim serTest = New SerializationTest(False)
    serTest.Deserialize(jsonPath)
    

    【讨论】:

    • 感谢一百万 @Jimi 的建议和编辑。顺便说一句,我知道 C# 和 VB.NET 数组是从零开始的,但我经常跳到 FORTRAN,这不是从零开始的。我确实想测试你后面的代码部分,它将反序列化的值复制到类对象中,因为我不想通过循环来增加装饰,而是直接填充数组。二进制是否重要,因为它最初用于将 I/O 通过管道传输到磁盘并返回?我不确定(即将被弃用的)BinaryFormatter 的二进制读取/写入是否比通过 streamreader/streamwriter 的 NewtonSoft.Json 更快?
    • 所有文件本质上都是二进制文件,我们看到的text只是对内容的一种解释。当然,使用 StreamReader 可能比仅在没有编码器的情况下读取字节值要慢一些,如果我们需要逐行读取(寻找行终止符),则要慢一点。否则,它只是一个 FileStream(StreamReader/Writer 当然在内部使用 FileStream,这就是为什么我写了不需要显式打开一个)- BinaryFormatter 执行许多与程序集兼容性相关的检查,这些检查是不必要的,其中之一没有人再使用它的原因。
    猜你喜欢
    • 2017-07-27
    • 2020-07-13
    • 2012-03-06
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    • 2017-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多