SharpSerializer:从反序列化中忽略 attributes/properties

SharpSerializer: Ignore attributes/properties from deserialization

我正在使用 SharpSerializer 到 serialize/deserialize 对象。

我希望能够在反序列化时忽略特定属性。

SharpSerializer 可以选择按属性或按 类 和 属性 名称忽略属性:

SharpSerializerSettings.AdvancedSettings.AttributesToIgnore
SharpSerializerSettings.AdvancedSettings.PropertiesToIgnore

但这些设置似乎只用于忽略序列化,而不是反序列化(我用GitHub 源代码和 NugetPackage)。

我说得对吗?

有什么方法可以从反序列化中忽略 attributes/properties 吗?

P.S.

  1. 我敢肯定还有其他很棒的序列化库,但是更改代码和所有现有的序列化文件需要花费大量精力。
  2. 我在GitHub项目上开了一个issue,但是这个项目好像从2018年开始就没有活动了
  3. 具有要忽略的属性的对象不必是根对象。

你是正确的 SharpSerializer does not implement ignoring of property values when deserializing. This can be verified from the reference source for ObjectFactory.fillProperties(object obj, IEnumerable<Property> properties):

private void fillProperties(object obj, IEnumerable<Property> properties)
{
    foreach (Property property in properties)
    {
        PropertyInfo propertyInfo = obj.GetType().GetProperty(property.Name);
        if (propertyInfo == null) continue;

        object value = CreateObject(property);
        if (value == null) continue;

        propertyInfo.SetValue(obj, value, _emptyObjectArray);
    }
}

此代码使用反射无条件地将从序列化流读取的任何 属性 设置到传入对象中,而不检查忽略的属性或属性列表。

因此,忽略所需属性的唯一方法似乎是像往常一样创建自己的 XmlPropertyDeserializer or BinaryPropertyDeserializer that skip or filter the unwanted properties. The following is one possible implementation for XML. This implementation reads the properties from XML into a Property 层次结构版本,然后应用过滤操作以删除与具有自定义属性的 .NET 属性相对应的属性[SharpSerializerIgnoreForDeserialize] 应用,然后最终使用修剪后的 Property.

创建对象树
[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class SharpSerializerIgnoreForDeserializeAttribute : System.Attribute { }

public class PropertyDeserializerDecorator : IPropertyDeserializer
{
    readonly IPropertyDeserializer deserializer;
    public PropertyDeserializerDecorator(IPropertyDeserializer deserializer) => this.deserializer = deserializer ?? throw new ArgumentNullException();

    public virtual void Open(Stream stream) => deserializer.Open(stream);
    public virtual Property Deserialize() => deserializer.Deserialize();
    public virtual void Close() => deserializer.Close();
}

public class CustomPropertyDeserializer : PropertyDeserializerDecorator
{
    Action<Property> deserializePropertyAction;
    public CustomPropertyDeserializer(IPropertyDeserializer deserializer, Action<Property> deserializePropertyAction = default) : base(deserializer) => this.deserializePropertyAction = deserializePropertyAction;
    public override Property Deserialize()
    {
        var property = base.Deserialize();

        if (deserializePropertyAction != null)
            property.WalkProperties(p => deserializePropertyAction(p));
        
        return property;
    }
}

public static partial class SharpSerializerExtensions
{
    public static SharpSerializer Create(SharpSerializerXmlSettings settings, Action<Property> deserializePropertyAction = default)
    {
        // Adapted from https://github.com/polenter/SharpSerializer/blob/42f9a20b3934a7f2cece356cc8116a861cec0b91/SharpSerializer/SharpSerializer.cs#L139
        // By https://github.com/polenter
        var typeNameConverter = settings.AdvancedSettings.TypeNameConverter ??
                                               new TypeNameConverter(
                                                   settings.IncludeAssemblyVersionInTypeName,
                                                   settings.IncludeCultureInTypeName,
                                                   settings.IncludePublicKeyTokenInTypeName);
        // SimpleValueConverter
        var simpleValueConverter = settings.AdvancedSettings.SimpleValueConverter ?? new SimpleValueConverter(settings.Culture, typeNameConverter);
        // XmlWriterSettings
        var xmlWriterSettings = new XmlWriterSettings
        {
            Encoding = settings.Encoding,
            Indent = true,
            OmitXmlDeclaration = true,
        };
        // XmlReaderSettings
        var xmlReaderSettings = new XmlReaderSettings
        {
            IgnoreComments = true,
            IgnoreWhitespace = true,
        };
        
        // Create Serializer and Deserializer
        var reader = new DefaultXmlReader(typeNameConverter, simpleValueConverter, xmlReaderSettings);
        var writer = new DefaultXmlWriter(typeNameConverter, simpleValueConverter, xmlWriterSettings);

        var _serializer = new XmlPropertySerializer(writer);
        var _deserializer = new CustomPropertyDeserializer(new XmlPropertyDeserializer(reader), deserializePropertyAction);
        
        var serializer = new SharpSerializer(_serializer, _deserializer)
        {
            //InstanceCreator = settings.InstanceCreator ?? new DefaultInstanceCreator(), -- InstanceCreator not present in SharpSerializer 3.0.1 
            RootName = settings.AdvancedSettings.RootName,
        };
        serializer.PropertyProvider.PropertiesToIgnore = settings.AdvancedSettings.PropertiesToIgnore;
        serializer.PropertyProvider.AttributesToIgnore = settings.AdvancedSettings.AttributesToIgnore;
        
        return serializer;
    }
    
    public static void WalkProperties(this Property property, Action<Property> action)
    {
        if (action == null || property == null)
            throw new ArgumentNullException();

        // Avoid cyclic dependencies.
        // Reference.IsProcessed is true only for the first reference of an object.
        bool skipProperty = property is ReferenceTargetProperty refTarget
            && refTarget.Reference != null
            && !refTarget.Reference.IsProcessed;

        if (skipProperty) return;

        action(property);

        switch (property.Art)
        {
            case PropertyArt.Collection:
                {
                    foreach (var item in ((CollectionProperty)property).Items)
                        item.WalkProperties(action);
                }
                break;
            case PropertyArt.Complex:
                {
                    foreach (var item in ((ComplexProperty)property).Properties)
                        item.WalkProperties(action);
                }
                break;
            case PropertyArt.Dictionary:
                {
                    foreach (var item in ((DictionaryProperty)property).Items)
                    {
                        item.Key.WalkProperties(action);
                        item.Value.WalkProperties(action);
                    }
                }
                break;
            case PropertyArt.MultiDimensionalArray:
                {
                    foreach (var item in ((MultiDimensionalArrayProperty )property).Items)
                        item.Value.WalkProperties(action);
                }
                break;
            case PropertyArt.Null:
            case PropertyArt.Simple:
            case PropertyArt.Reference:
                break;
            case PropertyArt.SingleDimensionalArray:
                {
                    foreach (var item in ((SingleDimensionalArrayProperty)property).Items)
                        item.WalkProperties(action);
                }
                break;
            default:
                throw new NotImplementedException(property.Art.ToString());
        }
    }
    
    public static void RemoveIgnoredChildProperties(Property p)
    {
        if (p.Art == PropertyArt.Complex)
        {
            var items = ((ComplexProperty)p).Properties;
            for (int i = items.Count - 1; i >= 0; i--)
            {
                if (p.Type.GetProperty(items[i].Name)?.IsDefined(typeof(SharpSerializerIgnoreForDeserializeAttribute), true) == true)
                {
                    items.RemoveAt(i);
                }
            }
        }
    }
}

然后,给出以下模型:

public class Root
{
    public List<Model> Models { get; set; } = new ();
}

public class Model
{
    public string Value { get; set; }
    
    [SharpSerializerIgnoreForDeserialize]
    public string IgnoreMe { get; set; }
}

您将使用自定义 XmlPropertyDeserializer 反序列化,如下所示:

var settings = new SharpSerializerXmlSettings();
var customSerialzier = SharpSerializerExtensions.Create(settings, SharpSerializerExtensions.RemoveIgnoredChildProperties);
var deserialized = (Root)customSerialzier.Deserialize(stream);

如果需要二进制反序列化,请改用以下工厂方法创建序列化程序:

public static partial class SharpSerializerExtensions
{
    public static SharpSerializer Create(SharpSerializerBinarySettings settings, Action<Property> deserializePropertyAction = default)
    {
        // Adapted from https://github.com/polenter/SharpSerializer/blob/42f9a20b3934a7f2cece356cc8116a861cec0b91/SharpSerializer/SharpSerializer.cs#L168
        // By https://github.com/polenter
        var typeNameConverter = settings.AdvancedSettings.TypeNameConverter ??
                                               new TypeNameConverter(
                                                   settings.IncludeAssemblyVersionInTypeName,
                                                   settings.IncludeCultureInTypeName,
                                                   settings.IncludePublicKeyTokenInTypeName);

        // Create Serializer and Deserializer
        Polenter.Serialization.Advanced.Binary.IBinaryReader reader;
        Polenter.Serialization.Advanced.Binary.IBinaryWriter writer;
        if (settings.Mode == BinarySerializationMode.Burst)
        {
            // Burst mode
            writer = new BurstBinaryWriter(typeNameConverter, settings.Encoding);
            reader = new BurstBinaryReader(typeNameConverter, settings.Encoding);
        }
        else
        {
            // Size optimized mode
            writer = new SizeOptimizedBinaryWriter(typeNameConverter, settings.Encoding);
            reader = new SizeOptimizedBinaryReader(typeNameConverter, settings.Encoding);
        }
        
        var _serializer = new BinaryPropertySerializer(writer);
        var _deserializer = new CustomPropertyDeserializer(new BinaryPropertyDeserializer(reader), deserializePropertyAction);
        
        var serializer = new SharpSerializer(_serializer, _deserializer)
        {
            //InstanceCreator = settings.InstanceCreator ?? new DefaultInstanceCreator(), -- InstanceCreator not present in SharpSerializer 3.0.1 
            RootName = settings.AdvancedSettings.RootName,
        };
        serializer.PropertyProvider.PropertiesToIgnore = settings.AdvancedSettings.PropertiesToIgnore;
        serializer.PropertyProvider.AttributesToIgnore = settings.AdvancedSettings.AttributesToIgnore;
        
        return serializer;
    }
}

然后做:

var settings = new SharpSerializerBinarySettings();
var customSerialzier = SharpSerializerExtensions.Create(settings, SharpSerializerExtensions.RemoveIgnoredChildProperties);
var deserialized = (Root)customSerialzier.Deserialize(stream);

备注:

演示 fiddle #1 here for XML, and #2 here 二进制。