【发布时间】: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];
}
【问题讨论】: