JSON:将数据行序列化为父对象

JSON:将数据行序列化为父对象,json,json.net,Json,Json.net,我有一个树形结构,从DataTable中的行创建,每个DataRow生成一个节点 每个节点当然应该包含子节点,但也包含源数据行: public class TreeNode { public List<TreeNode> Items { get; internal set; } public JToken RowJToken { get; internal set; } } 其中“[children]”表示节点的子节点(为简洁

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

每个节点当然应该包含子节点,但也包含源数据行:

    public class TreeNode
    {
        public List<TreeNode> Items { get; internal set; }
        public JToken RowJToken { get; internal set; }
    }
其中“[children]”表示节点的子节点(为简洁起见,省略)

然而,结果却是:

{
  "items": [children],
  "rowJToken": {
    "columnA": "ValueA",
    "columnB": "ValueB",
    "columnC": "ValueC"
  }
}
问题:如何将RowJToken内容序列化为与Items数组“在同一级别”,即不嵌入“RowJToken”对象

DataTable中的列集合事先是未知的(但我知道列名不会与“items”冲突)

下面的代码片段。请注意,在这种情况下,树结构本身的构造并不重要。 我在树节点中存储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];
        }
公共类树节点
{
公共JToken RowJToken{get;内部集;}
公共列表项{get;internal set;}
}
...
List treeNodes=GetTreeNodes(dataTable、childColumnName、parentColumnName);
DefaultContractResolver contractResolver=新的DefaultContractResolver
{
NamingStrategy=新CamelCaseNamingStrategy()
};
string json=JsonConvert.SerializeObject(树节点,新JsonSerializerSettings
{
ContractResolver=ContractResolver,
格式化=格式化。缩进
});
...
私有列表GetTreeNodes(DataTable DataTable、string childColumnName、string parentColumnName)
{
DataColumn childColumn=dataTable.Columns[childColumnName];
DataColumn parentColumn=dataTable.Columns[parentColumnName];
//  
//使用DataTable的序列化程序生成包含每个DataRow的JSON数组
//  
var jArray=jArray.FromObject(dataTable,JsonSerializer.CreateDefault(新的JsonSerializerSettings{NullValueHandling=NullValueHandling.Ignore}));
//  
//“NullObject”的使用仅仅是允许空值作为字典的键
//  
字典哈希=新字典();
var-rowIndex=0;
foreach(dataTable.Rows中的DataRow r)
{
字符串childId=r.Field(childColumn);
字符串parentId=r.Field(parentColumn);
if(!hash.TryGetValue(childId,out List childIdTreeNodes))
{
childIdTreeNodes=新列表();
Add(childId,childIdTreeNodes);
}
if(!hash.TryGetValue(parentId,out List parentIdTreeNodes))
{
parentIdTreeNodes=新列表();
Add(parentId,parentIdTreeNodes);
}
//  
//将与此数据行对应的JToken放入树节点
//  
var rowJToken=jArray[rowIndex++];
parentIdTreeNodes.Add(新TreeNode()
{
Items=childIdTreeNodes,
RowJToken=RowJToken
});
}
//  
//返回根节点,即父ID为NULL的节点
//  
返回哈希值[null];
}

您只需做一些简单的更改即可获得所需的输出

  • TreeNode
    类中,将
    RowJToken
    的类型从
    JToken
    更改为
    JObject
    。(为了保持一致,您可能还希望将属性名称更改为
    RowJObject
    ,但严格来说这不是必需的。)


  • [JsonExtensionData]
    属性装饰
    RowJToken
    属性

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

    var rowJToken = (JObject)jArray[rowIndex++];
    
  • 就这样。序列化
    treeNodes
    列表时,每个
    TreeNode
    RowJToken
    属性将与其相应的
    数组处于同一级别


    概念验证:

    太好了,谢谢,效果非常好!使用JsonExtensionData属性和所有属性时,我非常接近,但是使用JToken时,我得到了一个错误;很高兴我能帮忙
    [JsonExtensionData]
    不能与
    JToken
    一起工作,因为
    JToken
    不能实现
    IDictionary
    。但是
    JObject
    可以。事实证明,您的
    JArray
    中已经有了
    JObjects
    ,所以这只是更改类型和执行演员阵容的问题。
    [JsonExtensionData]
    public JObject RowJToken { get; internal set; }
    
    var rowJToken = (JObject)jArray[rowIndex++];