为了让 Json.Net 反序列化为 DataSet,JSON 必须采用特定格式,如 this answer 中所述。您的 JSON 很接近,但问题是 ACL 对象。 Json.Net 5.0 使用的 DataTableConverter 要求表中的所有列都是简单数据类型,否则将抛出 ArgumentOutOfRangeException (source)。 Json.Net 6.0 除了简单类型外,还支持嵌套数据表和数组,但是您的 ACL 数据仍然不符合允许将其正确反序列化为 DataSet 的所需格式。你有几个不同的选择来处理这个问题:
更改 JSON
如果您控制 JSON 的格式(即它不是来自第三方),您可以更改它,以便 Json.Net 6.0 能够将其反序列化为 DataSet。以下是它需要的样子:
{
"results": [
{
"templateName": "HUD Section 8",
"userID": "2",
"mobileObjectId": "4582",
"source": "M",
"inspectionType": "A",
"notes": "Window in bedroom needs repair.",
"agencyID": "",
"requestDate": "2014-05-09 00:00:00",
"agencyName": "",
"inspectionTimeBegun": "2014-05-09 14:00:17",
"inspectionDate": "2014-05-09 14:30:00",
"inspectionID": 135,
"inspectionTimeComplete": "2014-05-09 14:29:25",
"summaryDecision": "F",
"createdAt": "2014-05-09T18:29:35.050Z",
"updatedAt": "2014-05-09T18:29:35.050Z",
"objectId": "1FgtD6WT8Y",
"ACL": [
{
"user": "*",
"read": true,
"write": false
},
{
"user": "cryZoU5gXJ",
"read": true,
"write": true
}
]
}
]
}
使用这种格式,results 表的ACL 列将包含一个嵌套的DataTable,其中包含单独的 ACL 行,每行包含三列,user、read 和 write。
反序列化为强类型类
您可以反序列化为一组强类型类,而不是反序列化为DataSet。这种方法的优点是一切都是易于使用的形式。缺点是您需要先了解 JSON 中的内容,然后才能创建类。
您可以使用像 json2csharp.com 这样的第三方工具来帮助从 JSON 样本生成类,正如另一个答案(现已删除)中所建议的那样,但请注意,这不是万无一失的(而且它不是做VB)。有时您需要手动干预和编辑类。例如,如果从您的问题中的 JSON 生成类,您会注意到它为每个 ACL 实例创建一个固定类。除非您的 ACL 集始终恰好有两个项目,否则这将不起作用,一个称为 Everyone,另一个称为 CryZoU5gXJ。我认为 ACL 集更有可能是可变的,因此对这些使用 Dictionary 是有意义的。以下是我建议的课程:
Class RootObject
Public Property results As List(Of Result)
End Class
Class Result
Public Property templateName As String
Public Property userID As String
Public Property mobileObjectId As String
Public Property source As String
Public Property inspectionType As String
Public Property notes As String
Public Property agencyID As String
Public Property requestDate As String
Public Property agencyName As String
Public Property inspectionTimeBegun As String
Public Property inspectionDate As String
Public Property inspectionID As Integer
Public Property inspectionTimeComplete As String
Public Property summaryDecision As String
Public Property createdAt As String
Public Property updatedAt As String
Public Property objectId As String
Public Property ACL As Dictionary(Of String, ACL)
End Class
Class ACL
Public Property read As Boolean
Public Property write As Boolean
End Class
有了这个类结构,你可以像这样反序列化:
Dim root As RootObject = JsonConvert.DeserializeObject(Of RootObject)(strJSON)
对于 ACL,每个字典条目的键将是用户 ID(或 *,如您在示例中所使用的)。如果您实际上并不关心 ACL,您可以简单地从 Result 类中省略 ACL 属性。默认情况下,Json.Net 会跳过 JSON 中存在但类中不存在的属性。
使用 LINQ-to-JSON API 解析 JSON
使用 Json.Net 总是有不止一种方法可以给猫剥皮。 Json.Net 的LINQ-to-JSON API 在您解析的 JSON 高度可变和/或您不想创建用于接收数据的类时真的很出色。您可以将任何有效的 JSON 反序列化为 JToken 对象的层次结构,然后根据需要将它们分开。例如,如果您只需要从每个结果中选择几条信息,您可以这样做:
Dim token As JToken = JToken.Parse(json)
For Each result As JObject In token("results").Children(Of JObject)()
Console.WriteLine("userID: " + result("userID").ToString())
Console.WriteLine("templateName: " + result("templateName").ToString())
Console.WriteLine("inspectionID: " + result("inspectionID").ToString())
Console.WriteLine("inspectionType: " + result("inspectionType").ToString())
Console.WriteLine("inspectionDate: " + result("inspectionDate").ToString())
Console.WriteLine("summaryDecision: " + result("summaryDecision").ToString())
Console.WriteLine("notes: " + result("notes").ToString())
Next
您可以使用相同的方法从 JSON 手动构建 DataSet。这是一个通用函数,它将 JSON 反序列化为 DataSet,但忽略任何复杂对象(例如 ACL)而不是抛出异常:
Function DeserializeToDataSet(json As String) As DataSet
Dim root As JObject = JObject.Parse(json)
Dim ds As DataSet = New DataSet()
For Each prop As JProperty In root.Properties
If prop.Value.Type = JTokenType.Array Then
Dim dt As DataTable = ds.Tables.Add(prop.Name)
For Each row As JObject In prop.Value.Children(Of JObject)()
Dim dr As DataRow = dt.NewRow
For Each col As JProperty In row.Properties
Dim colType As Type = GetColumnType(col.Value.Type)
If Not colType Is Nothing Then
Dim dc As DataColumn = dt.Columns(col.Name)
If dc Is Nothing Then
dc = dt.Columns.Add(col.Name, colType)
End If
dr(col.Name) = col.Value.ToObject(colType)
End If
Next
dt.Rows.Add(dr)
Next
End If
Next
Return ds
End Function
Function GetColumnType(tokenType As JTokenType) As Type
If tokenType = JTokenType.String Then Return GetType(String)
If tokenType = JTokenType.Integer Then Return GetType(Integer)
If tokenType = JTokenType.Date Then Return GetType(DateTime)
If tokenType = JTokenType.Boolean Then Return GetType(Boolean)
If tokenType = JTokenType.Float Then Return GetType(Double)
Return Nothing
End Function
当然,如果您需要 ACL,则需要自定义此方法以将该数据放入您的代码可以使用的表单中。我会把这部分留给你。