简体   繁体   English

使用C#,可以在不定义大量类型的情况下查询YAML吗?

[英]With C#, is querying YAML possible without defining lots of types?

I need to work with YAML generated by Kubernetes and I'd like to be able to read specific properties with an XPath-like or jq -like DSL notation in C#. 我需要使用Kubernetes生成的YAML,我希望能够在C#中使用类似XPath或类似jq的DSL表示法读取特定属性。

The structure and nature of the YAML that Kubernetes generates is well-defined in most places, but in some cases is arbitrary and comes from user input, so it's not possible to define static types up front that can capture the entire structure of the YAML. Kubernetes生成的YAML的结构和性质在大多数地方都是明确定义的,但在某些情况下是任意的并且来自用户输入,因此不可能预先定义可以捕获YAML的整个结构的静态类型。

The most popular solution for deserializing and reading YAML in C# seems to be YamlDotNet , but it's mostly geared towards deserializing into fully-typed objects. 在C#中反序列化和读取YAML的最流行的解决方案似乎是YamlDotNet ,但它主要用于反序列化为完全类型的对象。

I'd rather not have to define a bunch of static types or do a lot of cumbersome casting just to get one or two fields or aggregate across them. 我宁愿不必定义一堆静态类型或做很多繁琐的转换只是为了得到一个或两个字段或聚合它们。 My ideal approach would instead be something like: 我的理想方法是:

var reader = new FileReader("my-file.yaml");
List<string> listOfPodNames = Yaml.Deserialize(reader)
                                  .Query(".pods[*].name")
                                  .AsList;
// expected result: list of all pod names as strings

Is this possible with YamlDotNet or another similar and well-supported tool in C#? 这可能与YamlDotNet或其他类似且受到良好支持的C#工具有关吗?

Update: I tried a number of approaches, but in the end, the one that worked best was reserializing to JSON and then querying with Json.NET, which has better support. 更新:我尝试了很多方法,但最后,最好的方法是重新编译为JSON,然后查询Json.NET,它有更好的支持。

When using YamlDotNet Deserializing mechanism without specifying a target type, we always get a either a Dictionary (mapping),a List of KeyValuePairs (list) or a single KeyValuePair/string (scalar). 当使用YamlDotNet反序列化机制而不指定目标类型时,我们总是得到一个Dictionary (映射),一个KeyValuePairs列表(列表)或一个KeyValuePair / string(标量)。 The KeyValuePairs will either contain another Dictionary, another List or the actual value. KeyValuePairs将包含另一个Dictionary,另一个List或实际值。

We now can implement a query functionality: 我们现在可以实现查询功能:

var data = new YamlQuery(yamlObject)
                        .On("pods")  // parent
                      // this functionality could be implemented as well wihtout much effort
                      //.Where("ignore").Equals(true)
                        .Get("name") // propery
                        .ToList<string>();

Edit: Multiple nested values 编辑:多个嵌套值

var data = new YamlQuery(yamlObject)
                .On("ressources")
                .On("pods")
                .Get("name")
                .ToList<string>();

Working example: https://dotnetfiddle.net/uNQPyl 工作示例: https//dotnetfiddle.net/uNQPyl

using System.IO;
using System;
using System.Linq;
using YamlDotNet.Serialization;
using System.Collections.Generic;
using YamlDotNet.RepresentationModel;

namespace ConsoleApp1
{
    public class Program
    {
        public static void Main()
        {
            object yamlObject;
            using (var r = new StringReader(Program.Document))
                yamlObject = new Deserializer().Deserialize(r);

            var data = new YamlQuery(yamlObject)
                                .On("pods")
                                .Get("name")
                                .ToList<string>();
            Console.WriteLine("all names of pods");
            Console.WriteLine(string.Join(",", data));


            data = new YamlQuery(yamlObject)
                    .On("ressources")
                    .On("pods")
                    .Get("name")
                    .ToList<string>();
            Console.WriteLine("all names of pods in ressources");
            Console.WriteLine(string.Join(",", data));

        }

        public class YamlQuery
        {
            private object yamlDic;
            private string key;
            private object current;

            public YamlQuery(object yamlDic)
            {
                this.yamlDic = yamlDic;
            }

            public YamlQuery On(string key)
            {
                this.key = key;
                this.current = query<object>(this.current ?? this.yamlDic, this.key, null);
                return this;
            }
            public YamlQuery Get(string prop)
            {
                if (this.current == null)
                    throw new InvalidOperationException();

                this.current = query<object>(this.current, null, prop, this.key);
                return this;
            }

            public List<T> ToList<T>()
            {
                if (this.current == null)
                    throw new InvalidOperationException();

                return (this.current as List<object>).Cast<T>().ToList();
            }

            private IEnumerable<T> query<T>(object _dic, string key, string prop, string fromKey = null)
            {
                var result = new List<T>();
                if (_dic == null)
                    return result;
                if (typeof(IDictionary<object, object>).IsAssignableFrom(_dic.GetType()))
                {
                    var dic = (IDictionary<object, object>)_dic;
                    var d = dic.Cast<KeyValuePair<object, object>>();

                    foreach (var dd in d)
                    {
                        if (dd.Key as string == key)
                        {
                            if (prop == null)
                            { 
                                result.Add((T)dd.Value);
                            } else
                            { 
                                result.AddRange(query<T>(dd.Value, key, prop, dd.Key as string));
                            }
                        }
                        else if (fromKey == key && dd.Key as string == prop)
                        { 
                            result.Add((T)dd.Value);
                        }
                        else
                        { 
                            result.AddRange(query<T>(dd.Value, key, prop, dd.Key as string));
                        }
                    }
                }
                else if (typeof(IEnumerable<object>).IsAssignableFrom(_dic.GetType()))
                {
                    var t = (IEnumerable<object>)_dic;
                    foreach (var tt in t)
                    {
                        result.AddRange(query<T>(tt, key, prop, key));
                    }

                }
                return result;
            }
        }




        private const string Document = @"---
            receipt:    Oz-Ware Purchase Invoice
            date:        2007-08-06
            customer:
                given:   Dorothy
                family:  Gale

            pods:
                - name:   pod1
                  descrip:   Water Bucket (Filled)
                  price:     1.47
                  quantity:  4


                - name:   pod2
                  descrip:   High Heeled ""Ruby"" Slippers
                  price:     100.27
                  quantity:  1
                - name:   pod3
                  descrip:   High Heeled ""Ruby"" Slippers
                  ignore:    true
                  quantity:  1

            bill-to:  &id001
                street: |-
                        123 Tornado Alley
                        Suite 16
                city:   East Westville
                state:  KS
                pods:
                    - name: pod4
                      descrip:   High Heeled ""Ruby"" Slippers
                      price:     100.27
                      quantity:  
            ressources:
                      - pids:
                            - id: 1
                            - name: pid
                      - pods: 
                            - name: pod5
                              descrip:   High Heeled ""Ruby"" Slippers
                              price:     100.27
                              quantity:  
                            - name: pod6
                              descrip:   High Heeled ""Ruby"" Slippers
                              price:     100.27
                              quantity:  
            specialDelivery: >
                Follow the Yellow Brick
                Road to the Emerald City.
                Pay no attention to the
                man behind the curtain.

...";
    }

}

Another approach you can use is converting YAML to JSON and then query it. 您可以使用的另一种方法是将YAML转换为JSON,然后进行查询。 though it would be a more time-consuming approach you can surely query a JSON easily then YAML. 虽然这将是一个更耗时的方法,你可以很容易地查询JSON然后YAML。

Here is how you can do it 这是你如何做到的

Convert YAML To JSON 将YAML转换为JSON

    public class ConvertYamlToJson
    {
        private readonly ITestOutputHelper output;

        public ConvertYamlToJson(ITestOutputHelper output)
        {
            this.output = output;
        }

        [Sample(
            DisplayName = "Convert YAML to JSON",
            Description = "Shows how to convert a YAML document to JSON."
        )]
        public void Main()
        {
            // convert string/file to YAML object
            var r = new StringReader(@"
scalar: a scalar
sequence:
  - one
  - two
");
            var deserializer = new DeserializerBuilder().Build();
            var yamlObject = deserializer.Deserialize(r);

            var serializer = new SerializerBuilder()
                .JsonCompatible()
                .Build();

            var json = serializer.Serialize(yamlObject);

            output.WriteLine(json);
        }
    }

Ref:- Convert YAML to JSON 参考: - 将YAML转换为JSON

Query JSON 查询JSON

 string json = @"
            {
              ""client_id"": ""26075235"",
              ""client_version"": ""1.0.0"",
              ""event"": ""app.uninstall"",
              ""timestamp"": 1478741247,
              ""data"": {
                ""user_id"": ""62581379"",
                ""site_id"": ""837771289247593785"",
                ""platform_app_id"": ""26075235""
              }
            }";

        JObject jo = JObject.Parse(json);

        Console.WriteLine("User ID: " + (string)jo.SelectToken("data.user_id"));

Ref:- JSON.NET JObject - how do I get value from this nested JSON structure 参考: - JSON.NET JObject - 如何从这个嵌套的JSON结构中获取值

There is the following GitHub project: YamlDotNet.Dynamic 有以下GitHub项目: YamlDotNet.Dynamic

It leverages the dynamic type in C# and therefore you don't need to define static types. 它利用C#中的动态类型,因此您无需定义静态类型。


A different approach would be to convert into Json and use Newtonsoft.Json, which supports dynamic types, too. 另一种方法是转换为Json并使用Newtonsoft.Json,它也支持动态类型。

What do you think about that? 你觉得怎么样? you can cast only the keys you need :) 你只能投出你需要的钥匙:)

List<Dictionary<string, object>> mydic = new Deserializer().Deserialize(r);
                foreach(Dictionary<string, object> wiii in mydic)
                {
                    bool value = false;
                    if (wiii.ContainsKey("yourKeyname"))
                        value = (bool)wiii["yourKeyname"]; //<-- Here you can cast it in the type you wish
                }

EDIT 编辑

Add the using at the start of your code: 在代码的开头添加使用:

using YamlDotNet.Serialization;
using YamlDotNet.RepresentationModel;

with that library added to your project and edit the code with the correct YAML deserialization, should be like the initial source code. 将该库添加到项目中并使用正确的YAML反序列化编辑代码,应该像初始源代码一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM