Как десериализовать YAML с полем без имени?

Есть такой YAML

681:
    activities:
        copying:
            time: 480
        manufacturing:
            materials:
            -   quantity: 86
                typeID: 38
            products:
            -   quantity: 1
                typeID: 165
            time: 600
        research_material:
            time: 210
        research_time:
            time: 210
    blueprintTypeID: 681
    maxProductionLimit: 300
682:
    activities:
        copying:
            time: 480
        manufacturing:
            materials:
            -   quantity: 133
                typeID: 38
            products:
            -   quantity: 1
                typeID: 166
            time: 600
        research_material:
            time: 210
        research_time:
            time: 210
    blueprintTypeID: 682
    maxProductionLimit: 300

Есть такие модели

  public class Root
    {
    public List<bl> bl { get; set; }
    
    
    }

    public class bl 
    {




    public activities activities { get; set; }



    }

    public class activities 
    {
        public copying copying { get; set; }
        public manufacturing manufacturing { get; set; }
        public research_material research_Material { get; set; }
        public research_time research_time { get; set; }
        public int blueprintTypeID { get; set; }
        public int maxProductionLimit { get; set; }


    }
    public class copying 
    {
    public int time { get; set; }



    }

    public class manufacturing 
    {

    public int time { get; set; }

        public materials materials { get; set; }
        public products products { get; set; }
    }

    public class materials 
    {
        public int quantity { get; set; }
        public int typeID { get; set; }


    }
    public class products 
    {
        public int quantity { get; set; }
        public int typeID { get; set; }

    }

    public class research_material 
    {
    public int time { get; set; }
    }

    public class research_time 
    {
        public int time { get; set; }


    }
}

Я использую YamlDotNet. Пытаюсь десериализовать

 string path = @"bl.yaml";
            string yaml;
          
            using (StreamReader reader = new StreamReader(path))
            {
                yaml = await reader.ReadToEndAsync();
            }
            var deserializer = new DeserializerBuilder().Build();

            
            var p = deserializer.Deserialize<Root>(yaml);

Получаю ошибку YamlDotNet.Core.YamlException: "Property '681' not found on type 'evesell.Root'." Пишет что я не добавил в модель поле для свойства '681'. Но как мне его добавить если у него нет имени?


Ответы (1 шт):

Автор решения: Alexander Petrov

Корнем этих данных является словарь. Поэтому в C# нужно использовать тип Dictionary<,>.

Заодно давайте исправим нейминг на общепринятый.
Пространства имён:

using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

Классы-модели:

public class Data
{
    public Activities Activities { get; set; }
    [YamlMember(Alias = "blueprintTypeID", ApplyNamingConventions = false)]
    public int BlueprintTypeId { get; set; }
    public int MaxProductionLimit { get; set; }
}
public class Activities
{
    public Copying Copying { get; set; }
    public Manufacturing Manufacturing { get; set; }
    [YamlMember(Alias = "research_material", ApplyNamingConventions = false)]
    public ResearchMaterial ResearchMaterial { get; set; }
    [YamlMember(Alias = "research_time", ApplyNamingConventions = false)]
    public ResearchTime ResearchTime { get; set; }
}
public class Copying
{
    public int Time { get; set; }
}
public class Manufacturing
{
    public int Time { get; set; }
    public Material[] Materials { get; set; }
    public Product[] Products { get; set; }
}
public class Material
{
    public int Quantity { get; set; }
    [YamlMember(Alias = "typeID", ApplyNamingConventions = false)]
    public int TypeId { get; set; }
}
public class Product
{
    public int Quantity { get; set; }
    [YamlMember(Alias = "typeID", ApplyNamingConventions = false)]
    public int TypeId { get; set; }
}
public class ResearchMaterial
{
    public int Time { get; set; }
}
public class ResearchTime
{
    public int Time { get; set; }
}

Bl - странное название для класса, я его назвал Data. Но это неважно.

Все свойства в C# именуются в стиле PascalCase - с большой буквы.
Поля в показанном yaml имеют стиль camelCase - с маленькой буквы.
Поэтому задаём правило именования CamelCaseNamingConvention.

Часть полей в yaml имеют знаки подчёркивания в названии: стиль snake_case или UnderscoredNamingConvention. Насколько я понял, совместить два стиля одновременно нет возможности. Поэтому на такие свойства пришлось навесить атрибуты YamlMember с указанием алиаса имени.
А также этот атрибут добавляем на свойства с суффиксом Id - в C# в соответствии с Framework Design Guidelinges он пишется именно так, а в yaml задано ID.

Названия классов Material и Product - в единственном числе. А вот названия свойств - во множественном: Materials, Products, т. к. это коллекции. Вместо массивов можно использовать любой другой подходящий тип коллекции, например, List<>.

Часть классов имеют одинаковый набор свойств. Возможно, следует сделать всего один общий класс. Но это вам виднее.


В итоге код десериализации с добавлением конвенции выглядит так:

string yaml = await File.ReadAllTextAsync(path);

var deserializer = new DeserializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .Build();

var dictionary = deserializer.Deserialize<Dictionary<int, Data>>(yaml);
→ Ссылка