我试图在VB.net 4.5中将一些json转换为自定义对象
Json结构可以有多个不同名称的元素,名称可能不一样,所以我不能为每个可能的名称创建一个静态类,因为我不知道它们会是什么。所有子元素的结构都是一样的。我不能控制json的结构,它来自另一个应用程序。
下面是JSON的简化版本:
{
"Item1": {
"config": {
"PortInfo" : {
"baud": 19200,
"dataLength": 8,
"flowControl": 0,
"parity": 0
},
"Custom": {
"116": 1,
"117": 2,
"129": 85,
"123": 1,
"124": 0,
"125": 2
}
}
},
"Item3": {
"config": {
"PortInfo" : {
"baud": 19200,
"dataLength": 8,
"flowControl": 0,
"parity": 0
},
"Custom": {
"116": 1,
"117": 2,
"129": 85,
"123": 1,
"124": 0,
"125": 2
}
}
},
"Item4": {
"config": {
"PortInfo" : {
"baud": 19200,
"dataLength": 8,
"flowControl": 0,
"parity": 0
},
"Custom": {
"116": 1,
"117": 2,
"129": 85,
"123": 1,
"124": 0,
"125": 2
}
}
},
"Errors": [
[
"com2",
"port busy"
],
[
"com4",
"port busy"
]
],
"timestamp": "2023-11-13 15:31:29"
}
字符串
我创建了以下类来保存数据:
Public Class Rootobject
Public Property Item As ItemObj
Public Property Errors As String()
Public Property timestamp As String
End Class
Public Class ItemObj
Public Property config As Config
End Class
Public Class Config
Public Property PortInfo As Portinfo
Public Property Custom As Dictionary(Of String, Integer)
End Class
Public Class Portinfo
Public Property baud As Integer
Public Property dataLength As Integer
Public Property flowControl As Integer
Public Property parity As Integer
End Class
型
我不知道如何将这个JSON转换为我的对象,导致
Item
元素的字典(或某种列表)- 单个
Errors
阵列 - 单个
timestamp
元素
我得到的最接近的是如果我从JSON中删 debugging 误和时间戳(所以只有item
s,然后我可以用result = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Dictionary(Of String, ItemObj))(Jsonstring)
填充对象(基于答案here)。如果我留下Errors
和timestamp
元素,那么我会得到一个异常:
无法将当前JSON数组(例如[1,2,3])转换为类型“PrinterAudit.RootObject”,因为该类型需要JSON对象(例如{“name”:“value”})才能正确转换。
如果所有的元素都是自己的数组或元素,那么这就很容易了,但是事实上,Errors
和timestamp
元素与Item
s处于同一级别,这真的让我很困惑。
编辑
使用dbc的解决方案,我尝试转换为VB,得到以下转换器:
Imports System.Runtime.CompilerServices
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports Newtonsoft.Json.Serialization
<AttributeUsage((AttributeTargets.Field Or AttributeTargets.Property), AllowMultiple:=False)>
Public Class JsonTypedExtensionDataAttribute
Inherits Attribute
End Class
Public Class TypedExtensionDataConverter(Of TObject)
Inherits JsonConverter
Public Overrides Function CanConvert(ByVal objectType As Type) As Boolean
Return GetType(TObject).IsAssignableFrom(objectType)
End Function
Private Function GetExtensionJsonProperty(ByVal contract As JsonObjectContract) As JsonProperty
Try
Return contract.Properties.Where(Function(p) p.AttributeProvider.GetAttributes(GetType(JsonTypedExtensionDataAttribute), False).Any()).[Single]()
Catch ex As InvalidOperationException
Throw New JsonSerializationException(String.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex)
End Try
End Function
Public Overrides Function ReadJson(ByVal reader As JsonReader, ByVal objectType As Type, ByVal existingValue As Object, ByVal serializer As JsonSerializer) As Object
If (reader.TokenType = JsonToken.Null) Then
Return Nothing
End If
Dim jObj = JObject.Load(reader)
Dim contract = CType(serializer.ContractResolver.ResolveContract(objectType), JsonObjectContract)
Dim extensionJsonProperty = Me.GetExtensionJsonProperty(contract)
Dim extensionJProperty = CType(Nothing, JProperty)
Dim i As Integer = (jObj.Count - 1)
Do While (i >= 0)
Dim prop = CType(jObj.AsList(i), JProperty)
If contract.Properties.GetClosestMatchProperty(prop.Name) Is Nothing Then
If (extensionJProperty Is Nothing) Then
extensionJProperty = New JProperty(extensionJsonProperty.PropertyName, New JObject)
jObj.Add(extensionJProperty)
End If
CType(extensionJProperty.Value, JObject).Add(prop.RemoveFromLowestPossibleParent)
End If
i = (i - 1)
Loop
Dim value = If(existingValue, contract.DefaultCreator())
Using subReader = jObj.CreateReader
serializer.Populate(subReader, value)
End Using
Return value
End Function
Public Overrides Sub WriteJson(ByVal writer As JsonWriter, ByVal value As Object, ByVal serializer As JsonSerializer)
Dim contract = CType(serializer.ContractResolver.ResolveContract(value.GetType), JsonObjectContract)
Dim extensionJsonProperty = Me.GetExtensionJsonProperty(contract)
Dim jObj As JObject
Using New PushValue(Of Boolean)(True, Function() Disabled, Function(canWrite) CSharpImpl.__Assign(Disabled, canWrite))
jObj = JObject.FromObject(value, serializer)
End Using
Dim extensionValue = CType(jObj(extensionJsonProperty.PropertyName), JObject).RemoveFromLowestPossibleParent
If (Not (extensionValue) Is Nothing) Then
Dim i As Integer = (extensionValue.Count - 1)
Do While (i >= 0)
Dim prop = CType(extensionValue.AsList(i), JProperty)
jObj.Add(prop.RemoveFromLowestPossibleParent)
i = (i - 1)
Loop
End If
jObj.WriteTo(writer)
End Sub
Private Class CSharpImpl
<Obsolete("Please refactor calling code to use normal Visual Basic assignment")>
Shared Function __Assign(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Class
<ThreadStatic()>
Private Shared _disabled As Boolean
' Disables the converter in a thread-safe manner.
Private Property Disabled As Boolean
Get
Return _disabled
End Get
Set
_disabled = Value
End Set
End Property
Public Overrides ReadOnly Property CanWrite As Boolean
Get
Return Not Me.disabled
End Get
End Property
Public Overrides ReadOnly Property CanRead As Boolean
Get
Return Not Me.disabled
End Get
End Property
End Class
Public Structure PushValue(Of T)
Implements IDisposable
Private setValue As Action(Of T)
Private oldValue As T
Public Sub New(ByVal value As T, ByVal getValue As Func(Of T), ByVal setValue As Action(Of T))
If getValue Is Nothing OrElse setValue Is Nothing Then Throw New ArgumentNullException()
Me.setValue = setValue
Me.oldValue = getValue()
setValue(value)
End Sub
#Region "IDisposable Members"
Public Sub Dispose() Implements IDisposable.Dispose
If (Not (Me.setValue) Is Nothing) Then
setValue(Me.oldValue)
End If
End Sub
#End Region
End Structure
Public Module JsonExtensions
<Extension()>
Public Function RemoveFromLowestPossibleParent(Of TJToken As JToken)(ByVal node As TJToken) As TJToken
If node Is Nothing Then Return Nothing
Dim contained = node.AncestorsAndSelf().Where(Function(t) TypeOf t.Parent Is JContainer AndAlso t.Parent.Type <> JTokenType.[Property]).FirstOrDefault()
If contained IsNot Nothing Then contained.Remove()
' Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
If TypeOf node.Parent Is JProperty Then CType(node.Parent, JProperty).Value = Nothing
Return node
End Function
<Extension()>
Public Function AsList(ByVal container As IList(Of JToken)) As IList(Of JToken)
Return container
End Function
End Module
型
对于我的生活,我不知道如何重构“过时”的CSharpImpl__Assign(Of T)
函数,所以我把它留在那里。我不确定这是否有影响。
下面是现在更新为使用转换器的类:
Imports Newtonsoft.Json
<JsonConverter(GetType(TypedExtensionDataConverter(Of Rootobject)))>
Public Class Rootobject
Public Sub New()
Item = New Dictionary(Of String, ItemObj)
End Sub
<JsonTypedExtensionData>
Public Property Item As Dictionary(Of String, ItemObj)
<JsonProperty("Errors")>
Public Property Errors As List(Of List(Of String))
<JsonProperty("timestamp")>
Public Property timestamp As String
End Class
Public Class ItemObj
Public Property config As Config
End Class
Public Class Config
Public Property PortInfo As Portinfo
Public Property Custom As Dictionary(Of String, Integer)
End Class
Public Class Portinfo
Public Property baud As Integer
Public Property dataLength As Integer
Public Property flowControl As Integer
Public Property parity As Integer
End Class
型
在我的主要方法中,我有以下内容:
Dim Jsonstr As String = "{""Item1"":{""config"":{""PortInfo"":{""baud"":19200,""dataLength"":8,""flowControl"":0,""parity"":0},""Custom"":{""116"":1,""117"":2,""123"":1,""124"":0,""125"":2,""129"":85}}},""Item3"":{""config"":{""PortInfo"":{""baud"":19200,""dataLength"":8,""flowControl"":0,""parity"":0},""Custom"":{""116"":1,""117"":2,""123"":1,""124"":0,""125"":2,""129"":85}}},""Item4"":{""config"":{""PortInfo"":{""baud"":19200,""dataLength"":8,""flowControl"":0,""parity"":0},""Custom"":{""116"":1,""117"":2,""123"":1,""124"":0,""125"":2,""129"":85}}},""Errors"":[[""com2"",""port busy""],[""com4"",""port busy""]],""timestamp"":""2023-11-13 15:31:29""}"
Dim result = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Rootobject)(Jsonstr)
型
在运行时,我得到错误:
System.InvalidCastException:“无法将类型为”System.Func“1[System.Object]”的对象强制转换为类型为“PrinterAudit. RootObject”。“
1条答案
按热度按时间mftmpeh81#
这是一个C#的例子,你可以在2个周期内完成,首先将常见的东西格式化,然后设置对象的数组(我曾经通过“项目”过滤-你可以找到一些像不是错误和不是时间戳之类的东西....)。
另外,你可以用字典代替列表,并添加名字作为参考键。
字符串
结果为:x1c 0d1x
UPDATE:你可以做反向逻辑:在类中有
型
在代码中:
型
所以结果会更合适
的