Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何将名称与某些模式匹配的JSON属性合并到数组值属性中?_C#_Json_Json.net - Fatal编程技术网

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]
应为元素[{element0,element1,element2}]

{
   "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"
}