aaubry / YamlDotNet

YamlDotNet is a .NET library for YAML
MIT License
2.53k stars 470 forks source link

How do I specify the tag for each item in the List #765

Open feelMySelfIsLouB opened 1 year ago

feelMySelfIsLouB commented 1 year ago

My sample code:

class Person
{
    public int Age = 0;
    public string Name = string.Empty;
}
 public static void SerializePerson()
{
    List<Person> persons = new List<Person>();
    persons.Add(new Person { Age = 10, Name = "1" });
    persons.Add(new Person { Age = 20, Name = "2" });
    persons.Add(new Person { Age = 30, Name = "3" });
    persons.Add(new Person { Age = 40, Name = "4" });

    var serializer = new SerializerBuilder()
   .WithNamingConvention(CamelCaseNamingConvention.Instance)
   .WithTagMapping("person", typeof(Person))
   .Build();

    string yamContent = serializer.Serialize(persons);
    Console.WriteLine(yamContent);
}

Console.WriteLine result:

- person
    age: 10
    name: 1
- person
    age: 20
    name: 2
- person
    age: 30
    name: 3
- person
    age: 40
    name: 4

I want each item to have a different tag just like this:

- person1
    age: 10
    name: 1
- person2
    age: 20
    name: 2
- person3
    age: 30
    name: 3
- person4
    age: 40
    name: 4

or tag uses an attribute of the Person class

What should I do to achieve this?Thank you!

EdwardCooke commented 1 year ago

That result is actually not valid YAML. Here's what the reference yaml parser returns for that (notice all the errors) http://ben-kiki.org/ypaste/data/64377/index.html

I'm assuming you mean this (colons on the end):

- person1:
    age: 10
    name: 1
- person2:
    age: 20
    name: 2
- person3:
    age: 30
    name: 3
- person4:
    age: 40
    name: 4

In which case, this works:

using System.Collections.Generic;
using YamlDotNet.Core;
using YamlDotNet.Serialization;

namespace ConsoleApp1
{
    public static class TypeConverterExample1
    {
        public static void Test()
        {
            var personList = new List<Person?>
            {
                new Person() { Id = "person1", Age = 10, Name = "1" },
                new Person() { Id = "person2", Age = 20, Name = "2" },
                new Person() { Id = "person3", Age = 30, Name = "3" },
                new Person() { Id = "person4", Age = 40, Name = "4" },
                null
            };

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

            var yaml = serializer.Serialize(personList);
            Console.WriteLine(yaml);
        }

        private class PersonTypeConverter : IYamlTypeConverter
        {
            public bool Accepts(Type type) => type == typeof(Person);

            public object? ReadYaml(IParser parser, Type type)
            {
                throw new NotImplementedException();
            }

            public void WriteYaml(IEmitter emitter, object? value, Type type)
            {
                var person = (Person?)value;
                if (person == null)
                {
                    emitter.Emit(new YamlDotNet.Core.Events.Scalar("null"));
                    return;
                }
                emitter.Emit(new YamlDotNet.Core.Events.MappingStart(new AnchorName(), new TagName(), false, YamlDotNet.Core.Events.MappingStyle.Any));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar(person.Id));

                emitter.Emit(new YamlDotNet.Core.Events.MappingStart(new AnchorName(), new TagName(), false, YamlDotNet.Core.Events.MappingStyle.Any));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar("age"));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar($"{person.Age}"));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar("name"));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar($"{person.Name}"));
                emitter.Emit(new YamlDotNet.Core.Events.MappingEnd());

                emitter.Emit(new YamlDotNet.Core.Events.MappingEnd());
            }
        }

        private class Person
        {
            public string Id { get; set; }
            public int Age { get; set; }
            public string Name { get; set; }
        }
    }
}
feelMySelfIsLouB commented 1 year ago

该结果实际上不是有效的 YAML。以下是引用 yaml 解析器为此返回的内容(注意所有错误)http://ben-kiki.org/ypaste/data/64377/index.html

我假设你的意思是这个(末尾的冒号):

- person1:
    age: 10
    name: 1
- person2:
    age: 20
    name: 2
- person3:
    age: 30
    name: 3
- person4:
    age: 40
    name: 4

在这种情况下,这有效:

using System.Collections.Generic;
using YamlDotNet.Core;
using YamlDotNet.Serialization;

namespace ConsoleApp1
{
    public static class TypeConverterExample1
    {
        public static void Test()
        {
            var personList = new List<Person?>
            {
                new Person() { Id = "person1", Age = 10, Name = "1" },
                new Person() { Id = "person2", Age = 20, Name = "2" },
                new Person() { Id = "person3", Age = 30, Name = "3" },
                new Person() { Id = "person4", Age = 40, Name = "4" },
                null
            };

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

            var yaml = serializer.Serialize(personList);
            Console.WriteLine(yaml);
        }

        private class PersonTypeConverter : IYamlTypeConverter
        {
            public bool Accepts(Type type) => type == typeof(Person);

            public object? ReadYaml(IParser parser, Type type)
            {
                throw new NotImplementedException();
            }

            public void WriteYaml(IEmitter emitter, object? value, Type type)
            {
                var person = (Person?)value;
                if (person == null)
                {
                    emitter.Emit(new YamlDotNet.Core.Events.Scalar("null"));
                    return;
                }
                emitter.Emit(new YamlDotNet.Core.Events.MappingStart(new AnchorName(), new TagName(), false, YamlDotNet.Core.Events.MappingStyle.Any));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar(person.Id));

                emitter.Emit(new YamlDotNet.Core.Events.MappingStart(new AnchorName(), new TagName(), false, YamlDotNet.Core.Events.MappingStyle.Any));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar("age"));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar($"{person.Age}"));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar("name"));
                emitter.Emit(new YamlDotNet.Core.Events.Scalar($"{person.Name}"));
                emitter.Emit(new YamlDotNet.Core.Events.MappingEnd());

                emitter.Emit(new YamlDotNet.Core.Events.MappingEnd());
            }
        }

        private class Person
        {
            public string Id { get; set; }
            public int Age { get; set; }
            public string Name { get; set; }
        }
    }
}

Thank you very much. That's exactly what I wanted.

feelMySelfIsLouB commented 1 year ago

How do I deserialize them with tag? I tried to deserialize it directly, but I was told that Person does not have a person1 (tag, which is the Id value of the Person) attribute

- person1:
    age: 10
    name: 1
- person2:
    age: 20
    name: 2
- person3:
    age: 30
    name: 3
- person4:
    age: 40
    name: 4
feelMySelfIsLouB commented 1 year ago

I try to match the property name in the ReadYaml method against Parser.Current, but it is difficult to determine the hierarchy if my Person object contains a List type property.

private class Person
{
    public string Id { get; set; }
    public int Age { get; set; }
    public string Name { get; set; }

    public List<Person> Child { get; set; }
}