有一个大的JSON文件(大约一千行)。任务是更新现有的JProperty,或在结构中的特定位置添加新的JProperty。新文本的位置基于JToken.Path属性。例如,这是JSON的开始:
"JonSnow": {
"Direwolf": {
"Name": "Ghost",
"Color": "White",
}
}
"DanaerysTargaryen": {
"Dragons": {
"Dragon1": {
"Name": "Drogon",
}
}
"Hair": {
"Color": "White"
}
}
现在必须使用给定的JToken路径列表和相应值来更新JSON。
第一种可能性是,对应于该路径的JProperty可能已经存在,在这种情况下,该值需要更新。我已经使用JToken.Replace()
成功地实现了这一点。
第二种可能性是,JProperty尚不存在,需要添加。例如,我需要将"DanaerysTargaryen.Dragons.Dragon1.Color"
添加为值"Black"
。
我知道我可以使用JSON.Net Add()
方法,但是使用此方法时,JSON中只会缺少路径的最后一个子标记。例如,我可以使用
JObject ObjToUpdate= JObject.Parse(jsonText);
JObject Dragon = ObjToUpdate["DanaerysTargaryen"]["Dragons"]["Dragon1"] as JObject;
Dragon.Add("Color", "Black"));
但是,如果我需要添加值为"JonSnow.Weapon.Type"
的{{1}}怎么办?因为"Longsword"
作为JProperty尚不存在,所以需要将其与"Weapon"
一起添加。对于每个路径,未知是JSON中已经存在多少路径。如何对此进行参数化?
"Type" : "Longsword"
// from outside source: Dictionary<string, string> PathBasedDict
// key: Jtoken.Path (example: "JonSnow.Weapon.Type")
// value: new text to be added (example: "Longsword")
foreach(KeyValuePair entry in PathBasedDict)
{
string path = entry.Key;
string newText = entry.Value;
if (ObjToUpdate.SelectToken(path) != null)
{ ObjToUpdate.SelectToken(path).Replace(newText); }
else AddToJson(path, newText);
}
应该是什么样?遍历整个路径并检查每个可能的JProperty以查看它是否存在,然后在其下添加其余的内容似乎很麻烦。有一个更好的方法吗?我不知道任何Json.NET技巧吗?我什至不知道如何将迭代参数化。
答案 0 :(得分:2)
有几种解决方法。这是其中两个。
要与现有代码一起使用,请用'.'
分割路径,然后遍历它们。如果路径不存在,请使用Add
创建路径。否则,如果我们位于路径的最后一部分,则只需添加值即可。
var json = JObject.Parse(@"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Name"": ""Drogon""}},""Hair"": {""Color"": ""White""}}}");
var toAdd = "DanaerysTargaryen.Dragons.Dragon1.Color";
var valueToAdd = "Black";
var pathParts = toAdd.Split('.');
JToken node = json;
foreach (var pathPart in pathParts)
{
var partNode = node.SelectToken(pathPart);
if (partNode == null && pathPart != pathParts.Last())
{
((JObject)node).Add(pathPart, new JObject());
partNode = node.SelectToken(pathPart);
}
else if (partNode == null && pathPart == pathParts.Last())
{
((JObject)node).Add(pathPart, valueToAdd);
partNode = node.SelectToken(pathPart);
}
node = partNode;
}
Console.WriteLine(json.ToString());
否则,您可以创建一个单独的JObject
来代表要添加的节点,然后合并它们。
var json = JObject.Parse(@"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Name"": ""Drogon""}},""Hair"": {""Color"": ""White""}}}");
var toMerge = @"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Color"":""Black""}}}}";
var jsonToMerge = JObject.Parse(toMerge);
json.Merge(jsonToMerge);
Console.WriteLine(json.ToString());
答案 1 :(得分:1)
基于Heretic Monkey's answer的第一种方法,这是一种扩展方法:
public static class JObjectExtensions
{
/// <summary>
/// Replaces value based on path. New object tokens are created for missing parts of the given path.
/// </summary>
/// <param name="self">Instance to update</param>
/// <param name="path">Dot delimited path of the new value. E.g. 'foo.bar'</param>
/// <param name="value">Value to set.</param>
public static void ReplaceNested(this JObject self, string path, JToken value)
{
if (self is null)
throw new ArgumentNullException(nameof(self));
if (string.IsNullOrEmpty(path))
throw new ArgumentException("Path cannot be null or empty", nameof(path));
var pathParts = path.Split('.');
JToken currentNode = self;
for (int i = 0; i < pathParts.Length; i++)
{
var pathPart = pathParts[i];
var isLast = i == pathParts.Length - 1;
var partNode = currentNode.SelectToken(pathPart);
if (partNode is null)
{
var nodeToAdd = isLast ? value : new JObject();
((JObject)currentNode).Add(pathPart, nodeToAdd);
currentNode = currentNode.SelectToken(pathPart);
}
else
{
currentNode = partNode;
if (isLast)
currentNode.Replace(value);
}
}
}
}