如何使用 YamlDotNet 将 JSON 转换为 YAML

Posted

技术标签:

【中文标题】如何使用 YamlDotNet 将 JSON 转换为 YAML【英文标题】:How to convert JSON to YAML using YamlDotNet 【发布时间】:2016-07-03 19:55:58 【问题描述】:

我正在尝试使用 YamlDotNet 将 JSON 转换为 YAML。这是我的代码:

class Program

    static void Main(string[] args)
    
        var json = "\"swagger\":\"2.0\",\"info\":\"title\":\"UberAPI\",\"description\":\"MoveyourappforwardwiththeUberAPI\",\"version\":\"1.0.0\",\"host\":\"api.uber.com\",\"schemes\":[\"https\"],\"basePath\":\"/v1\",\"produces\":[\"application/json\"]";
        var swaggerDocument = JsonConvert.DeserializeObject(json);

        var serializer = new YamlDotNet.Serialization.Serializer();

        using (var writer = new StringWriter())
        
            serializer.Serialize(writer, swaggerDocument);
            var yaml = writer.ToString();
            Console.WriteLine(yaml);
        
    

这是我提供的 JSON:


   "swagger":"2.0",
   "info":
      "title":"UberAPI",
      "description":"MoveyourappforwardwiththeUberAPI",
      "version":"1.0.0"
   ,
   "host":"api.uber.com",
   "schemes":[
      "https"
   ],
   "basePath":"/v1",
   "produces":[
      "application/json"
   ]

这是我期望的 YAML:

swagger: '2.0'
info:
  title: UberAPI
  description: MoveyourappforwardwiththeUberAPI
  version: 1.0.0
host: api.uber.com
schemes:
  - https
basePath: /v1
produces:
  - application/json

但是,这是我得到的输出:

swagger: []
info:
  title: []
  description: []
  version: []
host: []
schemes:
- []
basePath: []
produces:
- []

我不知道为什么所有属性都是空数组。

我也尝试过这样的类型化反序列化和序列化:

var specification = JsonConvert.DeserializeObject<SwaggerDocument>(json);
...
serializer.Serialize(writer, swaggerDocument, typeof(SwaggerDocument));

但这会产生


非常感谢任何帮助。

【问题讨论】:

【参考方案1】:

您实际上不需要将 JSON 反序列化为强类型对象,您也可以使用动态 Expando 对象将 JSON 转换为 YAML。这是一个小例子:-

var json = @"
        'Name':'Peter',
        'Age':22,
        'CourseDet':
                'CourseName':'CS',
                'CourseDescription':'Computer Science',
                ,
        'Subjects':['Computer Languages','Operating Systems']
        ";

        var expConverter = new ExpandoObjectConverter();
        dynamic deserializedObject = JsonConvert.DeserializeObject<ExpandoObject>(json, expConverter);

        var serializer = new YamlDotNet.Serialization.Serializer();
        string yaml = serializer.Serialize(deserializedObject);

您可以看到这两种方法的详细说明,即使用强类型对象和动态对象here。

【讨论】:

【参考方案2】:

您可以将JObject 转换为 YamlDotNet 可以序列化的更简单的对象:

class Program

    static void Main(string[] args)
    
        var json = "\"swagger\":\"2.0\",\"info\":\"title\":\"UberAPI\",\"description\":\"MoveyourappforwardwiththeUberAPI\",\"version\":\"1.0.0\",\"host\":\"api.uber.com\",\"schemes\":[\"https\"],\"basePath\":\"/v1\",\"produces\":[\"application/json\"]";
        var swaggerDocument = ConvertJTokenToObject(JsonConvert.DeserializeObject<JToken>(json));

        var serializer = new YamlDotNet.Serialization.Serializer();

        using (var writer = new StringWriter())
        
            serializer.Serialize(writer, swaggerDocument);
            var yaml = writer.ToString();
            Console.WriteLine(yaml);
        
    

    static object ConvertJTokenToObject(JToken token)
    
        if (token is JValue)
            return ((JValue)token).Value;
        if (token is JArray)
            return token.AsEnumerable().Select(ConvertJTokenToObject).ToList();
        if (token is JObject)
            return token.AsEnumerable().Cast<JProperty>().ToDictionary(x => x.Name, x => ConvertJTokenToObject(x.Value));
        throw new InvalidOperationException("Unexpected token: " + token);
    

【讨论】:

【参考方案3】:

我认为当 json 反序列化返回 JObject 时有问题。看起来 yaml 序列化器不喜欢它。

正如你提到的JsonConvert.DeserializeObject&lt;SwaggerDocument&gt;(json),我使用了指定类型的反序列化,这就是我得到的

Swagger: 2.0
Info:
  Title: UberAPI
  Description: MoveyourappforwardwiththeUberAPI
  Version: 1.0.0
Host: api.uber.com
Schemes:
- https
BasePath: /v1
Produces:
- application/json

这是我的全部代码:

class Program

    static void Main(string[] args)
    
        var json = "\"Swagger\":\"2.0\",\"Info\":\"Title\":\"UberAPI\",\"Description\":\"MoveyourappforwardwiththeUberAPI\",\"Version\":\"1.0.0\",\"Host\":\"api.uber.com\",\"Schemes\":[\"https\"],\"BasePath\":\"/v1\",\"Produces\":[\"application/json\"]";
        var swaggerDocument = JsonConvert.DeserializeObject<SwaggerDocument>(json);
        
        var serializer = new YamlDotNet.Serialization.Serializer();

        using (var writer = new StringWriter())
        
            serializer.Serialize(writer, swaggerDocument);
            var yaml = writer.ToString();
            Console.WriteLine(yaml);
        
    


public class Info

    public string Title  get; set; 
    public string Description  get; set; 
    public string Version  get; set; 


public class SwaggerDocument

    public string Swagger  get; set; 
    public Info Info  get; set; 
    public string Host  get; set; 
    public List<string> Schemes  get; set; 
    public string BasePath  get; set; 
    public List<string> Produces  get; set; 

更新

这里有两个问题。

当反序列化带有字段的类时,默认情况下,json.net 在执行此工作时不会考虑它们。为此,我们必须通过创建自定义合约解析器来自定义反序列化过程。我们可以很容易地做到这一点

var swaggerDocument = JsonConvert.DeserializeObject<SwaggerDocument>(json, new JsonSerializerSettings

    ContractResolver = new MyContractResolver()
);

public class MyContractResolver : DefaultContractResolver

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    
        var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Select(p => base.CreateProperty(p, memberSerialization))
            .Union(type.GetFields(BindingFlags.Public | BindingFlags.Instance)
                .Select(f => base.CreateProperty(f, memberSerialization)))
            .ToList();
        props.ForEach(p =>  p.Writable = true; p.Readable = true; );
        return props;
    

当我们想用字段序列化类时,还有第二个问题:来自字段的值不会包含在 yaml 结果中。我还没想好怎么处理。

您是否必须使用Swashbuckle.Swagger 类型,或者您可以为此类型创建包装器/装饰器/DTO?

希望对你有帮助。

【讨论】:

【参考方案4】:

FWIW 我编写了一个 nuget 库,以使 YamlDotNet 与 Json.Net 一起工作,尊重所有 JSON.net 序列化属性。

Github:https://github.com/tomlm/YamlConvert Nuget 库:https://www.nuget.org/packages/YamlConvert/
    var yaml = YamlConvert.SerializeObject(obj);
    var obj2 = YamlConvert.DeserializeObject<T>(yaml);

它通过为 JTokens (JObject/JArray/JValue) 添加一个 YamlDotNet 类型序列化类来工作

    var serializer = new SerializerBuilder()
         .WithTypeConverter(new JTokenYamlConverter())
         .Build();

【讨论】:

【参考方案5】:

使用Cinchoo ETL - 一个可以轻松进行此类转换的开源库。

using (var r = new ChoJSONReader("*** YOUR JSON FILEPATH ***"))

    using (var w = new ChoYamlWriter("*** YAML FILE OUTPUT PATH ***").SingleDocument())
    
        w.Write(r);
    

输出:

swagger: 2.0
info:
  title: UberAPI
  description: MoveyourappforwardwiththeUberAPI
  version: 1.0.0
host: api.uber.com
schemes:
  - https
basePath: /v1
produces:
  - application/json

小提琴示例: https://dotnetfiddle.net/rbOD0o

免责声明:我是这个库的作者。

【讨论】:

【参考方案6】:

我正在使用以下代码从 JSON 构建 Yaml 元素并将其写入文件。

代码如下:

    public static void BuildParametrizedYAML(string element, string element1)
    
        var jsonBreakers = @"
        
            'watchers' : 
                'timer' : '10',
                'watcherPool' : '5',
                's3fileExtension' : '.avr.gz',
                'maxRetriesTask' : '3',
                'telemetryFolder' : '/data',
                'telemetryProcessor' :  
                    'url' : '"+ element1 + @"'
                ,
                'breakers' : 
                [
                    
                        'breakerId' : 'COMMANDER',
                        'firstRetryTimeout' : '1000',
                        'secondRetryTimeout' : '6000',
                        'retries' : '5'
                    ,
                    
                        'breakerId' : 'PROCESSOR',
                        'firstRetryTimeout' : '1000',
                        'secondRetryTimeout' : '6000',
                        'retries' : '30'
                    
                ],
                'servers' : 
                [
                    
                        'serverId' : 'vmax',
                        'url' : '"+ element + @"'
                    
                ]
            
        ";

        var expConverter = new ExpandoObjectConverter();
        dynamic deserializedObject = JsonConvert.DeserializeObject<ExpandoObject>(jsonBreakers, expConverter);           
        var serializer = new Serializer();
        string JSONContent = serializer.Serialize(deserializedObject);

        var streamLoad = new StringReader(JSONContent);
        var stream = new YamlStream();
        stream.Load(streamLoad);

        using (TextWriter writer = File.CreateText("application.yml"))
        
            stream.Save(writer, false);
        
    

这是输出:

watchers:
  timer: 10
  watcherPool: 5
  s3fileExtension: .avr.gz
  maxRetriesTask: 3
  telemetryFolder: /data
  telemetryProcessor:
    url: TELEMETRYPROCESSORURL
  breakers:
  - breakerId: COMMANDER
    firstRetryTimeout: 1000
    secondRetryTimeout: 6000
    retries: 5
  - breakerId: PROCESSOR
    firstRetryTimeout: 1000
    secondRetryTimeout: 6000
    retries: 30
  servers:
  - serverId: vmax
    url: TELEMETRYWATCHERVMAXURL
...

请随时给我写信。

【讨论】:

以上是关于如何使用 YamlDotNet 将 JSON 转换为 YAML的主要内容,如果未能解决你的问题,请参考以下文章

c#中,怎们从YAML文件读入数组?

如何使用 json.net 将 c# 通用列表转换为 json?

如何使用 json.net 将 c# 通用列表转换为 json?

如何使用 json.net 将数据表转换为 json 字符串?

如何使用 PHP 将 HTML 转换为 JSON?

如何使用 GSON 将 List 转换为 JSON 对象?