代码之家  ›  专栏  ›  技术社区  ›  John Feminella

使用C,在不定义许多类型的情况下查询yaml是否可行?

  •  9
  • John Feminella  · 技术社区  · 6 年前

    我需要使用kubernetes生成的yaml,我希望能够使用类似xpath或 jq -就像c中的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 回复  |  直到 5 年前
        1
  •  4
  •   codeteq    6 年前

    使用yamldotnet反序列化机制而不指定目标类型时, 词典 (列表)或单个键值对/字符串(标量)。

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

    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.
    
    ...";
        }
    
    }
    
        2
  •  2
  •   Mihir Dave    6 年前

    您可以使用的另一种方法是将yaml转换为json,然后查询它。虽然这将是一种更耗时的方法,但您肯定可以轻松地查询JSON,然后查询YAML。

    将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

        3
  •  1
  •   johannespartin    6 年前

    有以下Github项目: YamlDotNet.Dynamic

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


    另一种方法是转换成JSON并使用newtonsoft.json,它也支持动态类型。

        4
  •  -1
  •   Legion    6 年前

    你觉得怎么样?您可以只铸造您需要的密钥:)

    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 YamlDotNet.Serialization;
    using YamlDotNet.RepresentationModel;
    

    具有 that library added to your project 并且用正确的yaml反序列化编辑代码,应该像初始的源代码一样。