C# 如何将名称与某些模式匹配的JSON属性合并到数组值属性中?
如何合并同一个键的对象,忽略数组包装器([]) 下面的示例行具有名为C# 如何将名称与某些模式匹配的JSON属性合并到数组值属性中?,c#,json,json.net,C#,Json,Json.net,如何合并同一个键的对象,忽略数组包装器([]) 下面的示例行具有名为“elements[0]”、“elements[1]”和“elements[2]”的属性 属性可以位于JSON结构的任何级别 多个数组元素,如元素[0]、[1]、[2]、[3]和另一个元素[0]、[1]、[2]、[3] 应为元素[{element0,element1,element2}] { "addresses":[ "some address" ],
“elements[0]”
、“elements[1]”
和“elements[2]”
的属性
- 属性可以位于JSON结构的任何级别
- 多个数组元素,如元素[0]、[1]、[2]、[3]和另一个元素[0]、[1]、[2]、[3]
{
"addresses":[
"some address"
],
"rows":[
{
"elements": [{
"distance": {
"text": "227 mi",
"value": 365468
},
"duration": {
"text": "3 hours 54 mins",
"value": 14064
},
"status": "OK"
},
{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
},
{
"distance": {
"text": "2,878 mi",
"value": 4632197
},
"duration": {
"text": "1 day 18 hours",
"value": 151772
},
"status": "OK"
}]}
],
"status":[
"OK"
]
}
需求位于未知的JSON字符串上,因此无法创建类/模型。以上只是一个例子,任何通用代码都会更有帮助
更新::
谢谢@dbc。看起来很有希望,顺序元素肯定会上升,但其他键会被交换
{
"elements[0]": "Value 0",
"elements[1]": "Value 1",
"anotherelement[0]": "Another value 0",
"anotherelement[1]": "Another value 1",
"status" : "OK",
"lastitem" : "yes"
}
结果如下。有没有办法按原样排列物品的顺序。我知道在JSON中它不会影响,只是想看看是否可能
{
"status" : "OK",
"lastitem" : "yes",
"elements": [
"Value 0",
"Value 1"
],
"anotherelement": [
"Another value 0",
"Another value 1"
]
}
预期是
{
"elements": [
"Value 0",
"Value 1"
],
"anotherelement": [
"Another value 0",
"Another value 1"
],
"status" : "OK",
"lastitem" : "yes"
}
为了重申您的问题,您有一个任意的JSON层次结构,其中包含名称以括号中的数字索引结尾的属性,如下所示(其中值可以是任何类型): 您可以将它们转换为数组值属性,方法是剥离括号内的索引,分组并将所有值与相同的剥离属性名称组合,如下所示:
{
"elements": [
"Value 0",
"Value 1"
],
"anotherelement": [
"Another value 0",
"Another value 1"
]
}
可以使用来编辑JSON层次结构。您还需要使用正则表达式来选择匹配的属性名称,并使用LINQ语句将具有类似名称的项分组在一起
以下扩展方法执行此任务:
public static partial class JsonExtensions
{
public static void FixElementArrays(this JToken root)
{
var regex = new Regex("^(.+)\\[[0-9]+\\]$");
if (root is JContainer container)
{
var query =
from o in container.DescendantsAndSelf().OfType<JObject>()
let matches = o.Properties()
.Select(p => (Property : p, Match : regex.Match(p.Name)))
.Where(m => m.Match.Success)
.Select(m => (m.Property, Name : m.Match.Groups[1].Value))
let groups = matches.GroupBy(m => m.Name)
from g in groups
select (Object : o, Name : g.Key, Values : g.Select(m => m.Property.Value).ToList());
foreach (var g in query.ToList())
{
IList<JToken> objAsList = g.Object;
// DescendantsAndSelf() returns items in document order, which ordering is preserved by GroupBy, so index of first item should be first index.
var insertIndex = objAsList.IndexOf(g.Values[0].Parent);
g.Values.ForEach(v => v.RemoveFromLowestPossibleParent());
objAsList.Insert(insertIndex, new JProperty(g.Name, new JArray(g.Values)));
}
}
}
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
// If the parent is a JProperty, remove that instead of the token itself.
var property = node.Parent as JProperty;
var contained = property ?? node;
if (contained.Parent != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (property != null)
property.Value = null;
return node;
}
}
注:
- 您可能需要根据实际的JSON属性名调整正则表达式。例如,您的问题不清楚如何处理像
这样的名称“[0][1]”
- 代码假定
属性的顺序已经正确,即不正确“elements[*]”
因此,将它们按遇到的顺序放入最终数组,而不是尝试按{ "elements[3]": "Value 3", "elements[1]": "Value 1", "elements[2]": "Value 2" }
属性名称中的索引号排序“elements[*]”
演示小提琴。您是否至少知道元素是按正确的顺序排列的,即在
“元素[0]”
之前不会得到“元素[1]”
?另外,您所说的和另一个元素[0]、[1]、[2]、[3]
是什么意思?你能举个例子吗?谢谢@dbc。看起来很有希望,order元素肯定会上升,但是其他键的顺序会被交换。我在更新部分的问题中添加了更多信息。@Bullet-Answer Updated。但是,将对象定义为一组无序的名称/值对,因此实际上不需要关心属性顺序。
public static partial class JsonExtensions
{
public static void FixElementArrays(this JToken root)
{
var regex = new Regex("^(.+)\\[[0-9]+\\]$");
if (root is JContainer container)
{
var query =
from o in container.DescendantsAndSelf().OfType<JObject>()
let matches = o.Properties()
.Select(p => (Property : p, Match : regex.Match(p.Name)))
.Where(m => m.Match.Success)
.Select(m => (m.Property, Name : m.Match.Groups[1].Value))
let groups = matches.GroupBy(m => m.Name)
from g in groups
select (Object : o, Name : g.Key, Values : g.Select(m => m.Property.Value).ToList());
foreach (var g in query.ToList())
{
IList<JToken> objAsList = g.Object;
// DescendantsAndSelf() returns items in document order, which ordering is preserved by GroupBy, so index of first item should be first index.
var insertIndex = objAsList.IndexOf(g.Values[0].Parent);
g.Values.ForEach(v => v.RemoveFromLowestPossibleParent());
objAsList.Insert(insertIndex, new JProperty(g.Name, new JArray(g.Values)));
}
}
}
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
// If the parent is a JProperty, remove that instead of the token itself.
var property = node.Parent as JProperty;
var contained = property ?? node;
if (contained.Parent != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (property != null)
property.Value = null;
return node;
}
}
var rootToken = JToken.Parse(jsonString);
rootToken.FixElementArrays();
var fixedJsonString = rootToken.ToString();
{
"elements[3]": "Value 3",
"elements[1]": "Value 1",
"elements[2]": "Value 2"
}