使用C#,是否可以在不定义很多类型的情况下查询YAML?

时间:2018-06-25 18:23:39

标签: c# yaml

我需要使用Kubernetes生成的YAML,并且希望能够使用C#中类似XPath或jq的DSL表示法读取特定属性。

Kubernetes生成的YAML的结构和性质在大多数地方都是定义明确的,但是在某些情况下是任意的,并且来自用户输入,因此无法预先定义可以捕获整个结构的静态类型。 YAML。

在C#中用于反序列化和读取YAML的最受欢迎的解决方案似乎是YamlDotNet,但它主要用于反序列化为完全类型的对象。

我宁愿不必定义一堆静态类型或进行大量繁琐的转换,而只需要获取一个或两个字段或在它们之间进行聚合。我理想的方法应该是:

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

是否可以使用YamlDotNet或C#中的另一个类似且受支持的工具?

更新:我尝试了多种方法,但最后,最有效的方法是重新序列化为JSON,然后使用具有更好支持的Json.NET进行查询。

4 个答案:

答案 0 :(得分:4)

在使用YamlDotNet反序列化机制而不指定目标类型时, 我们总是会得到一个词典(映射),一个 KeyValuePair列表(列表)或单个KeyValuePair /字符串(标量)。 KeyValuePair将包含另一个Dictionary,另一个List或实际值。

我们现在可以实现查询功能:

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>();

修改: 多个嵌套值

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

工作示例: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.

...";
    }

}

答案 1 :(得分:2)

您可以使用的另一种方法是将YAML转换为JSON,然后对其进行查询。尽管这将是一种更加耗时的方法,但是您可以肯定地比JSON轻松地查询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);
        }
    }

参考:-Convert YAML to 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"));

参考:-JSON.NET JObject - how do I get value from this nested JSON structure

答案 2 :(得分:1)

有以下GitHub项目: YamlDotNet.Dynamic

它利用C#中的动态类型,因此您不需要定义静态类型。


另一种方法是转换为Json并使用也支持动态类型的Newtonsoft.Json。

答案 3 :(得分:-1)

您对此有何看法?您只能投射所需的键:)

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
                }

编辑

在代码开头添加using:

using YamlDotNet.Serialization;
using YamlDotNet.RepresentationModel;

使用that library added to your project并使用正确的YAML反序列化来编辑代码,应类似于初始源代码。