【问题标题】:JSON: Serializing DataRow into parent objectJSON:将 DataRow 序列化为父对象
【发布时间】:2020-06-03 15:25:37
【问题描述】:

我有一个树形结构,由 DataTable 中的行创建,每个 DataRow 生成一个节点。

每个节点当然应该包含子节点,还要包含源DataRow:

    public class TreeNode
    {
        public List<TreeNode> Items { get; internal set; }
        public JToken RowJToken { get; internal set; }
    }

(RowJToken而不是DataRow的原因见文末代码)

当我序列化树结构时,我需要以这种方式序列化每个 TreeNode(这由消费客户端决定):

{
  "items": [children],
  "columnA": "ValueA",
  "columnB": "ValueB",
  "columnC": "ValueC"
}

其中“[children]”表示节点的子节点(为简洁起见省略)。

然而,结果却是:

{
  "items": [children],
  "rowJToken": {
    "columnA": "ValueA",
    "columnB": "ValueB",
    "columnC": "ValueC"
  }
}

问题:如何将 RowJToken 内容与 Items 数组“在同一级别”序列化,即不嵌入到“rowJToken”对象中?

事先不知道 DataTable 中的列集(但我知道列名不会与“项目”冲突)。

下面的代码片段。请注意,在这种情况下,树结构本身的构造不应该很重要。 我在 TreeNode 中存储 RowJToken 而不是 DataRow 的原因是 JSON.Net 没有 DataRow 序列化程序,而只有一个 DataTable 序列化程序。因此,我先将DataTable序列化为一个JArray,然后将DataRow对应的JToken,粘贴到TreeNode中。

    public class TreeNode
    {
        public JToken RowJToken { get; internal set; }
        public List<TreeNode> Items { get; internal set; }
    }

...

        List<TreeNode> treeNodes = GetTreeNodes(dataTable, childColumnName, parentColumnName);

        DefaultContractResolver contractResolver = new DefaultContractResolver
        {
            NamingStrategy = new CamelCaseNamingStrategy()
        };

        string json = JsonConvert.SerializeObject(treeNodes, new JsonSerializerSettings
        {
            ContractResolver = contractResolver,
            Formatting = Formatting.Indented
        });

...

        private List<TreeNode> GetTreeNodes(DataTable dataTable, string childColumnName, string parentColumnName)
        {
            DataColumn childColumn = dataTable.Columns[childColumnName];
            DataColumn parentColumn = dataTable.Columns[parentColumnName];

            //  
            //  Use the serializer for DataTable to generate a JSON array, containing each DataRow
            //  
            var jArray = JArray.FromObject(dataTable, JsonSerializer.CreateDefault(new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));

            //  
            //  The use of "NullObject" is merely to allow NULL values as the key to the dictionary
            //  
            Dictionary<NullObject<string>, List<TreeNode>> hash = new Dictionary<NullObject<string>, List<TreeNode>>();

            var rowIndex = 0;

            foreach (DataRow r in dataTable.Rows)
            {
                string childId = r.Field<string>(childColumn);
                string parentId = r.Field<string>(parentColumn);

                if (!hash.TryGetValue(childId, out List<TreeNode> childIdTreeNodes))
                {
                    childIdTreeNodes = new List<TreeNode>();
                    hash.Add(childId, childIdTreeNodes);
                }

                if (!hash.TryGetValue(parentId, out List<TreeNode> parentIdTreeNodes))
                {
                    parentIdTreeNodes = new List<TreeNode>();
                    hash.Add(parentId, parentIdTreeNodes);
                }

                //  
                //  Put the JToken which corresponds to this DataRow in the TreeNode
                //  
                var rowJToken = jArray[rowIndex++];
                parentIdTreeNodes.Add(new TreeNode()
                {
                    Items = childIdTreeNodes,
                    RowJToken = rowJToken
                });
            }

            //  
            //  Return the root node(s), i.e. the nodes with NULL as parent ID
            //  
            return hash[null];
        }

【问题讨论】:

    标签: json json.net


    【解决方案1】:

    你可以通过一些简单的改变得到你想要的输出。

    1. 在您的TreeNode 类中,将RowJToken 的类型从JToken 更改为JObject。 (您可能还想将属性名称更改为 RowJObject,以保持一致,但这不是绝对必要的。)

      public JObject RowJToken { get; internal set; }
      
    2. [JsonExtensionData] 属性装饰RowJToken 属性。

      [JsonExtensionData]
      public JObject RowJToken { get; internal set; }
      
    3. 在您的GetTreeNodes 方法中,将从jArray 检索到的JToken 转换为JObject

      var rowJToken = (JObject)jArray[rowIndex++];
      

    就是这样。当您序列化treeNodes 列表时,每个TreeNodeRowJToken 属性将与其各自的Items 数组处于同一级别。

    概念证明:https://dotnetfiddle.net/LEaeXo

    【讨论】:

    • 太好了,谢谢,效果很好!我真的很接近,使用 JsonExtensionData 属性和所有属性,但是使用 JToken 我得到了一个错误。
    • 没问题;很高兴我能帮上忙!
    • [JsonExtensionData] 不适用于JToken,因为JToken 没有实现IDictionary&lt;string, JToken&gt;。但是JObject 可以。事实证明,您的 JArray 中已经有 JObjects,因此只需更改类型并执行转换即可。
    猜你喜欢
    • 2019-03-01
    • 2010-10-08
    • 1970-01-01
    • 1970-01-01
    • 2016-12-22
    • 2018-07-20
    • 2021-12-16
    相关资源
    最近更新 更多