如何从斜杠分隔的字符串创建 JSON?

How to create JSON from a slash separated String?

我有一个斜杠分隔的字符串格式。

例如

  myDecision/data/buyerDecision/data/buy = food

  myDecision/data/sellerDecision/data/sell = food

我想把它转换成下面的json格式

"myDecision":{
    "data":{
        "buyerDesision":{
            "data":{"buy":"food"}
        },
        "sellerDesision":{
            "data":{"sell":"food"}
        }
    }
}

我尝试了不同的解决方案,但没有用。

想到的最简单的解决方案是:

  static async Task Main(string[] args)
    {
        var stringObjects = new string[]
        {
              "myDecision/data/buyerDecision/data/buy = food",
               "myDecision/data/sellerDecision/data/sell = food"
        };
        var objectList = new List<JObject>();
        foreach (var s in stringObjects)
        {
            objectList.Add(ParseStringObject(s));
        }
    }

    static JObject ParseStringObject(string s)
    {
        // TODO! normalize input string (trim white space)

        var openingBrackets = "{"; // top level braket
        var closingBrakets =  new string('}', s.Count(c => c == '/') + 1) ; // +1 for the top level braket
        var json = $"{openingBrackets}\"{s.Replace("/", "\": { \"").Replace("=", "\":\"")}\"{closingBrakets}";
        
        return JObject.Parse(json);
        // TODO! handle exceptions 

    }

您需要安装 Newtonsoft.Json 才能正常工作。

不过也可以使用其他递归方法。

更新: 要将所有字符串转换为单个对象,可以使用 JObject

支持的 Merge 方法
  static async Task Main(string[] args)
    {
        var stringObjects = new string[]
        {
              "myDecision/data/buyerDecision/data/buy = food",
               "myDecision/data/sellerDecision/data/sell = food"
        };
        var resultingObject = new JObject();
        var mergeSettings = new JsonMergeSettings
        {
            MergeArrayHandling = MergeArrayHandling.Union
        };
        foreach (var s in stringObjects)
        {
            var obj = ParseStringObject(s);
            resultingObject.Merge(obj,mergeSettings);
        }
        Console.WriteLine(resultingObject.ToString(Newtonsoft.Json.Formatting.Indented));
    }

    static JObject ParseStringObject(string s)
    {
        try
        {
           //remove whitespace
            s = Regex.Replace(s, @"\s+", "");
            var openingBrackets = "{"; // top level braket
            var closingBrakets = new string('}', s.Count(c => c == '/') + 1); // +1 for the top level braket
            var json = $"{openingBrackets}\"{s.Replace("/", "\": { \"").Replace("=", "\":\"")}\"{closingBrakets}";

            return JObject.Parse(json);
        }
        catch (Exception)
        {
            // return an empty object
            return new JObject();
        }
    }

这应该输出:

解决此问题的一种方法是手工制作 JObject,然后将它们合并。 (这不是最高效的解决方案,因为它会多次重新创建层次结构的某些级别。)

此方法不同于 Benzara Tahar 提出的解决方案:

  • Tahar:使用字符串操作构造一个有效的 json 然后使用 Json.Parse
  • 我的:使用路径遍历来构造JObject层次结构。

核心逻辑

核心逻辑可以这样实现:

static JObject CreateHierarchy(Queue<string> pathLevels, JObject currentNode)
{
    if (pathLevels.Count == 0) return currentNode;

    var newNode = new JObject(new JProperty(pathLevels.Dequeue(), currentNode));
    return CreateHierarchy(pathLevels, newNode);
}
  • 这是一个递归函数,它构建从最内层到最外层的层次结构。
  • 因此,如果我们使用以下 pathLevels 调用此方法:data, buyerDecision, data, myDescision
    然后它会生成这样的东西:
new JObject(
    new JProperty("myDecision",
        new JObject(
            new JProperty("data",
                new JObject(
                    new JProperty("buyerDecision",
                        new JObject(
                            new JProperty("data", currentNode)))))));

调用核心逻辑

首先,将输入的字符串转换为字典,其中

  • 关键是路径
  • 该值为所需的最内层值:
var dataSource = new List<string>
{
    "myDecision/data/buyerDecision/data/buy = food",
    "myDecision/data/sellerDecision/data/sell = food"
};

var mappings = dataSource.Select(data => data.Split('=', StringSplitOptions.RemoveEmptyEntries))
    .ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim());
  • 注意:这很脆弱,因为如果多个数据源条目想要设置相同的 属性.
  • ,它可能会抛出 ArgumentException: 'An item with the same key has already been added
  • 我们可以遍历字典以从条目构造 JObjects
    • key应该被/拆分然后逆向集合
    • 最里面的值和最里面的路径可以用来构造currentNode
var objectsWithHierarchy = new List<JObject>();
foreach (var (path, innerMostValue) in mappings.Select(kv => (kv.Key, kv.Value)))
{
    var entryLevels = path.Split('/').Reverse().ToArray();
    objectsWithHierarchy.Add(CreateHierarchy(new Queue<string>(entryLevels.Skip(1)),
        new JObject(new JProperty(entryLevels.First(), innerMostValue))));
}

合并对象

这里我们选择 objectsWithHierarchy 到集合中的第一个对象作为基础 JObject 我们 apply/merge 其余 JObject 的对象。

var baseObject = objectsWithHierarchy.First();
var mergeSettings = new JsonMergeSettings {MergeArrayHandling = MergeArrayHandling.Union};
foreach (var currentObj in objectsWithHierarchy.Skip(1))
{
    baseObject.Merge(currentObj, mergeSettings);
}

Console.WriteLine(baseObject);

输出如下:

{
  "myDecision": {
    "data": {
      "buyerDecision": {
        "data": {
          "buy": "food"
        }
      },
      "sellerDecision": {
        "data": {
          "sell": "food"
        }
      }
    }
  }
}

结束语

  • 这段代码真的很脆弱,因为它是根据两个样本数据设计的,没有实际需求
  • 数据格式非常独特,所以我们想使用 JSONPath
  • 实际用例也是未知的,因此解决方案本身可能会提供非常差的性能
    • 在层次结构非常深的情况下
    • 如果数据源非常大