hadashiA/VYaml

How to serialize [][] using Block and Flow style nesting

Closed this issue · 7 comments

I encountered some problems when implementing Matrix4x4Formatter. I would like to ask how to implement it.

First I implemented Vector4Formatter

using System.Collections.Generic;
using UnityEngine;
using VYaml.Emitter;
using VYaml.Parser;

namespace VYaml.Serialization
{
    public class Vector4Formatter : IYamlFormatter<Vector4>
    {
        public static readonly Vector4Formatter Instance = new();

        public void Serialize(ref Utf8YamlEmitter emitter, Vector4 value, YamlSerializationContext context)
        {
            var fs = new[] { value.x, value.y, value.z, value.w };
            emitter.BeginSequence(SequenceStyle.Flow);
            foreach (var f in fs) emitter.WriteFloat(f);
            emitter.EndSequence();
        }

        public Vector4 Deserialize(ref YamlParser parser, YamlDeserializationContext context)
        {
            if (parser.IsNullScalar())
            {
                parser.Read();
                return default;
            }

            var formatter = context.Resolver.GetFormatterWithVerify<List<float>>();

            var list = context.DeserializeWithAlias(formatter, ref parser);

            if (list.Count == 4) return new Vector4(list[0], list[1], list[2], list[3]);
            if (list.Count == 3) return new Vector4(list[0], list[1], list[2]);
            if (list.Count == 2) return new Vector4(list[0], list[1]);

            return default;
        }
    }
}

Then implement Matrix4x4Formatter based on this

using System.Collections.Generic;
using UnityEngine;
using VYaml.Emitter;
using VYaml.Parser;

namespace VYaml.Serialization
{
    public class Matrix4x4Formatter : IYamlFormatter<Matrix4x4>
    {
        public static readonly Matrix4x4Formatter Instance = new();

        public void Serialize(ref Utf8YamlEmitter emitter, Matrix4x4 value, YamlSerializationContext context)
        {
            var v4s = new[] { value.GetColumn(0), value.GetColumn(1), value.GetColumn(2), value.GetColumn(3) };
            context.Serialize(ref emitter, v4s);
            
            // emitter.BeginSequence();
            // foreach (var v4 in v4s)
            // {
            //     context.Serialize(ref emitter, v4);
            // }
            // emitter.EndSequence();
            
            // var fs = new[]
            // {
            //     new[] { value.m00, value.m10, value.m20, value.m30 },
            //     new[] { value.m01, value.m11, value.m21, value.m31 },
            //     new[] { value.m02, value.m12, value.m22, value.m32 },
            //     new[] { value.m03, value.m13, value.m23, value.m33 }
            // };
            //
            // emitter.BeginSequence();
            // foreach (var f in fs) Utils.WriteFloatArrayWithFlowStyle(ref emitter, f, context);
            // emitter.EndSequence();
        }

        public Matrix4x4 Deserialize(ref YamlParser parser, YamlDeserializationContext context)
        {
            if (parser.IsNullScalar())
            {
                parser.Read();
                return default;
            }

            var formatter = context.Resolver.GetFormatterWithVerify<List<Vector4>>();

            var list = context.DeserializeWithAlias(formatter, ref parser);

            if (list.Count == 4) return new Matrix4x4(list[0], list[1], list[2], list[3]);
            
            return default;
        }
    }
}

But the YAML string obtained is similar to this

  matrix4X4:   - [1, 0, 0, 0]
  - [0, 1, 0, 0]
  - [0, 0, 1, 0]
  - [0, 0, 0, 1]

As a result, it is impossible to deserialize

I solved the problem temporarily, but I think it is a bug and it should be automatic line wrapping that is correct

    public class Matrix4x4Formatter : IYamlFormatter<Matrix4x4>
    {
        public static readonly Matrix4x4Formatter Instance = new();

        public void Serialize(ref Utf8YamlEmitter emitter, Matrix4x4 value, YamlSerializationContext context)
        {
            var fs = new[]
            {
                new[] { value.m00, value.m10, value.m20, value.m30 },
                new[] { value.m01, value.m11, value.m21, value.m31 },
                new[] { value.m02, value.m12, value.m22, value.m32 },
                new[] { value.m03, value.m13, value.m23, value.m33 }
            };

            emitter.BeginSequence();
            emitter.WriteRaw(ReadOnlySpan<byte>.Empty, false, true);
            foreach (var f in fs) Utils.WriteFloatArrayWithFlowStyle(ref emitter, f, context);
            emitter.EndSequence();
        }

        public Matrix4x4 Deserialize(ref YamlParser parser, YamlDeserializationContext context)
        {
            if (parser.IsNullScalar())
            {
                parser.Read();
                return default;
            }

            var formatter = context.Resolver.GetFormatterWithVerify<List<Vector4>>();

            var list = context.DeserializeWithAlias(formatter, ref parser);

            if (list.Count == 4) return new Matrix4x4(list[0], list[1], list[2], list[3]);

            return default;
        }
    }
    public static void WriteFloatArrayWithFlowStyle(ref Utf8YamlEmitter emitter, IEnumerable<float> value,
      YamlSerializationContext context)
    {
      emitter.BeginSequence(SequenceStyle.Flow);
      foreach (var f in value) emitter.WriteFloat(f);
      emitter.EndSequence();
    }

Thanks for the report. I'll fix this later.

My relevant code is at VYaml.UnityResolvers, you can use Matrix4x4 or List<Vector3> for testing

@hadashiA

I have done some testing and the following code temporarily solves the issue, but it may not be completely correct. I hope this can be helpful.

case EmitState.BlockSequenceEntry:
{
var output = writer.GetSpan(currentIndentLevel * options.IndentWidth + BlockSequenceEntryHeader.Length + 1);
var offset = 0;
WriteIndent(output, ref offset);
BlockSequenceEntryHeader.CopyTo(output[offset..]);
offset += BlockSequenceEntryHeader.Length;
output[offset++] = YamlCodes.FlowSequenceStart;
writer.Advance(offset);
break;
}

                        case EmitState.BlockSequenceEntry:
                        {
                          switch (PreviousState)
                          {
                            case EmitState.BlockMappingValue:
                              if (IsFirstElement) WriteRaw1(YamlCodes.Lf);
                            break;
                            case EmitState.BlockSequenceEntry:
                              if (!IsFirstElement)
                                for (int i = 0; i < options.IndentWidth; i++)
                                  WriteRaw1(YamlCodes.Space); 
                              break;
                          }
                            var output = writer.GetSpan(currentIndentLevel * options.IndentWidth + BlockSequenceEntryHeader.Length + 1);
                            var offset = 0;
                            WriteIndent(output, ref offset);
                            BlockSequenceEntryHeader.CopyTo(output[offset..]);
                            offset += BlockSequenceEntryHeader.Length;
                            output[offset++] = YamlCodes.FlowSequenceStart;
                            writer.Advance(offset);
                            break;
                        }

@hadashiA There is another issue when serializing structures like List<float[][]>, you will get the following result:

- -[1, 0, 0, 0]
-[1, 0, 0, 0]
-[1, 0, 0, 0]
-[1, 0, 0, 0]
- -[1, 0, 0, 0]
-[1, 0, 0, 0]
-[1, 0, 0, 0]
-[1, 0, 0, 0]

@MonoLogueChi Thanks for the report.
I believe I fixed your test case in #93.
I'll probably release it as soon as I have a few more validations.

Thanks,My test is normal

https://github.com/u2sb/VYaml.UnityResolvers/blob/fc320766843a4b8052a6d51ab8c7caf351967506/Assets/Tests/VYamlTester.cs#L145-L173

output

Matrix4x4: 
- 
  - [-7.782528, 37.35536, -47.91584, 0]
  - [43.59058, 42.45243, -28.33409, 0]
  - [37.03134, -86.71092, -3.385229, 0]
  - [12.82171, 1.237767, -83.08399, 0]
- 
  - [2.874351, -47.73241, -64.72952, 0]
  - [34.91239, -64.36443, -55.1876, 0]
  - [65.48726, -39.25167, -24.1115, 0]
  - [28.50582, 6.619106, -85.29636, 0]

https://github.com/u2sb/VYaml.UnityResolvers/blob/fc320766843a4b8052a6d51ab8c7caf351967506/Assets/Tests/VYamlTester.cs#L99-L116

output

RectOffset: 
- [97, 93, 94, 30]
- [8, 47, 91, 2]