如何跳过Yamldotnet中的空集合

时间:2019-06-02 00:11:43

标签: yamldotnet

我正在尝试找出如何跳过使用YamlDotNet序列化空集合。我已经尝试了自定义 ChainedObjectGraphVisitor IYamlTypeConverter 。我是使用YamlDotNet的新手,这里有些知识空白。

以下是我对访客模式的实现,这导致YamlDotNet.Core.YamlException “预期的SCALAR,SEQUENCE-START,MAPPING-START或ALIAS出现了MappingEnd”错误。我确实看到了一些有关MappingStart / MappingEnd的在线内容,但是我不确定它是否适合我想要做的事情(消除了许多空集合中的混乱情况)。任何朝着正确方向的指针都值得赞赏。

实例化序列化器:

var serializer = new YamlDotNet.Serialization.SerializerBuilder()
                .WithNamingConvention(new YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention())
                .WithEmissionPhaseObjectGraphVisitor(args => new YamlIEnumerableSkipEmptyObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer))
                .Build();

ChainedObjectGraphVisitor实现:

    public sealed class YamlIEnumerableSkipEmptyObjectGraphVisitor : ChainedObjectGraphVisitor
{
    private readonly ObjectSerializer nestedObjectSerializer;
    private readonly IEnumerable<IYamlTypeConverter> typeConverters;

    public YamlIEnumerableSkipEmptyObjectGraphVisitor(IObjectGraphVisitor<IEmitter> nextVisitor, IEnumerable<IYamlTypeConverter> typeConverters, ObjectSerializer nestedObjectSerializer)
        : base(nextVisitor)
    {
        this.typeConverters = typeConverters != null
            ? typeConverters.ToList()
            : Enumerable.Empty<IYamlTypeConverter>();

        this.nestedObjectSerializer = nestedObjectSerializer;
    }

    public override bool Enter(IObjectDescriptor value, IEmitter context)
    {
        bool retVal;

        if (typeof(System.Collections.IEnumerable).IsAssignableFrom(value.Value.GetType()))
        {   // We have a collection
            var enumerableObject = (System.Collections.IEnumerable)value.Value;
            if (enumerableObject.GetEnumerator().MoveNext()) // Returns true if the collection is not empty.
            {   // Serialize it as normal.
                retVal = base.Enter(value, context);
            }
            else
            {   // Skip this item.
                retVal = false;
            }
        }
        else
        {   // Not a collection, normal serialization.
            retVal = base.Enter(value, context);
        }

        return retVal;
    }
}

2 个答案:

答案 0 :(得分:1)

我相信答案是还要用类似于Enter()方法的逻辑覆盖基类中的EnterMapping()方法:

        public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context)
    {
        bool retVal = false;

        if (value.Value == null)
            return retVal;

        if (typeof(System.Collections.IEnumerable).IsAssignableFrom(value.Value.GetType()))
        {   // We have a collection
            var enumerableObject = (System.Collections.IEnumerable)value.Value;
            if (enumerableObject.GetEnumerator().MoveNext()) // Returns true if the collection is not empty.
            {   // Don't skip this item - serialize it as normal.
                retVal = base.EnterMapping(key, value, context);
            }
            // Else we have an empty collection and the initialized return value of false is correct.
        }
        else
        {   // Not a collection, normal serialization.
            retVal = base.EnterMapping(key, value, context);
        }

        return retVal;
    }

答案 1 :(得分:0)

我上了以下课:

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

sealed class YamlIEnumerableSkipEmptyObjectGraphVisitor : ChainedObjectGraphVisitor
{
    public YamlIEnumerableSkipEmptyObjectGraphVisitor(IObjectGraphVisitor<IEmitter> nextVisitor): base(nextVisitor)
    {
    }

    private bool IsEmptyCollection(IObjectDescriptor value)
    {
        if (value.Value == null)
            return true;

        if (typeof(IEnumerable).IsAssignableFrom(value.Value.GetType()))
            return !((IEnumerable)value.Value).GetEnumerator().MoveNext();

        return false;
    }

    public override bool Enter(IObjectDescriptor value, IEmitter context)
    {
        if (IsEmptyCollection(value))
            return false;

        return base.Enter(value, context);
    }

    public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context)
    {
        if (IsEmptyCollection(value))
            return false;

        return base.EnterMapping(key, value, context);
    }
}