You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JToken.FromObject生成JSON时单行子DataTable为数组,需改为对象格式求助

问题:DataTable序列化为JSON时,单条数据的子表需转为单个对象而非数组

原VB代码

Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
    Dim dtTICKET As New DataTable
    dtTICKET.Columns.Add("Title", GetType(String))
    dtTICKET.Columns.Add("Queue", GetType(String))
    
    dtTICKET.Rows.Add("New Updates", "Updates")
    'dtTICKET.Rows.Add("Lost connection", "Network")
    
    Dim dtJSON As New DataTable
    dtJSON.Columns.Add("UserLogin", GetType(String))
    dtJSON.Columns.Add("Password", GetType(String))
    dtJSON.Columns.Add("Ticket", GetType(DataTable))
    
    dtJSON.Rows.Add("user@domain.de", "pwd", dtTICKET)
    
    json = JToken.FromObject(dtJSON).ToString()     
End Sub

当前输出的JSON

[
  {
    "UserLogin": "user@domain.de",
    "Password": "pwd",
    "Ticket": [
      {
        "Title": "New Updates",
        "Queue": "Updates"
      }
    ]
  }
]

需求

当dtTICKET仅包含一行数据时,Ticket字段需以**单个对象(大括号包裹)**呈现,而非数组(方括号包裹);当有多行时则保持数组形式。之前尝试过自定义JsonSerializer并设置Formatting.IndentChar,但不确定方向是否正确及具体实现方法,求解决方案。


解决方案

可以通过自定义JsonConverter动态判断子表行数,决定序列化为数组还是单个对象,具体实现如下:

1. 编写自定义JsonConverter

Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports System.Data

Public Class DataTableSingleOrArrayConverter
    Inherits JsonConverter(Of DataTable)

    Public Overrides Sub WriteJson(writer As JsonWriter, value As DataTable, serializer As JsonSerializer)
        If value Is Nothing Then
            writer.WriteNull()
            Return
        End If

        ' 将DataTable转换为JArray
        Dim jArray = JArray.FromObject(value, serializer)
        
        ' 根据行数决定输出单个对象还是数组
        If jArray.Count = 1 Then
            jArray(0).WriteTo(writer)
        Else
            jArray.WriteTo(writer)
        End If
    End Sub

    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As DataTable, hasExistingValue As Boolean, serializer As JsonSerializer) As DataTable
        ' 反序列化逻辑(支持双向转换)
        Dim token = JToken.Load(reader)
        If token.Type = JTokenType.Array Then
            Return token.ToObject(Of DataTable)(serializer)
        Else
            Dim jArray = New JArray()
            jArray.Add(token)
            Return jArray.ToObject(Of DataTable)(serializer)
        End If
    End Function
End Class

2. 修改原代码,应用自定义转换器

在序列化时给JsonSerializerSettings添加自定义转换器:

Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
    Dim dtTICKET As New DataTable
    dtTICKET.Columns.Add("Title", GetType(String))
    dtTICKET.Columns.Add("Queue", GetType(String))
    
    dtTICKET.Rows.Add("New Updates", "Updates")
    'dtTICKET.Rows.Add("Lost connection", "Network")
    
    Dim dtJSON As New DataTable
    dtJSON.Columns.Add("UserLogin", GetType(String))
    dtJSON.Columns.Add("Password", GetType(String))
    dtJSON.Columns.Add("Ticket", GetType(DataTable))
    
    dtJSON.Rows.Add("user@domain.de", "pwd", dtTICKET)
    
    ' 配置序列化规则,添加自定义转换器
    Dim settings As New JsonSerializerSettings()
    settings.Converters.Add(New DataTableSingleOrArrayConverter())
    settings.Formatting = Formatting.Indented ' 可选,格式化输出
    
    json = JsonConvert.SerializeObject(dtJSON, settings)
End Sub

效果验证

  • 当dtTICKET只有1行时,Ticket输出为单个对象:
[
  {
    "UserLogin": "user@domain.de",
    "Password": "pwd",
    "Ticket": {
      "Title": "New Updates",
      "Queue": "Updates"
    }
  }
]
  • 当dtTICKET有多行时,Ticket保持数组形式,与原输出一致。

说明

自定义转换器的核心是在序列化DataTable时,判断其转换后的JArray长度,决定输出单个元素还是整个数组。如果不需要支持反序列化,可以简化ReadJson方法(比如直接抛出NotImplementedException)。


内容的提问来源于stack exchange,提问作者AndreasA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 00:50:21