【问题标题】:SharpSerializer: Ignore attributes/properties from deserializationSharpSerializer:从反序列化中忽略属性/属性
【发布时间】:2021-12-18 18:00:38
【问题描述】:

我正在使用SharpSerializer 序列化/反序列化对象。

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

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

SharpSerializerSettings.AdvancedSettings.AttributesToIgnore
SharpSerializerSettings.AdvancedSettings.PropertiesToIgnore

但似乎这些设置仅用于从 序列化 中忽略,而不是从 反序列化 中忽略(我使用 GitHub 源代码和 NugetPackage 进行了测试)。

我说的对吗?

有什么方法可以忽略反序列化的属性/属性?

附言

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

【问题讨论】:

  • 有问题的对象总是根对象吗?
  • @dbc: 不,它不是根对象

标签: c# xml serialization deserialization sharpserializer


【解决方案1】:

SharpSerializer 在反序列化时没有实现忽略属性值是正确的。这可以从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);
    }
}

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

因此,忽略所需属性的唯一方法似乎是创建自己的 XmlPropertyDeserializerBinaryPropertyDeserializer 版本,以跳过或过滤不需要的属性。以下是 XML 的一种可能实现。此实现照常将 XML 中的属性读取到 Property 层次结构中,然后应用过滤器操作以删除与应用了自定义属性 [SharpSerializerIgnoreForDeserialize] 的 .NET 属性相对应的属性,最后使用修剪后的 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);

注意事项:

演示小提琴 #1 here 用于 XML,#2 here 用于二进制。

【讨论】:

  • 谢谢!这很有帮助!我遇到了一个对象具有循环依赖关系的问题。我解决了它并相应地在您的答案中编辑了WalkProperties 方法。
猜你喜欢
  • 2013-04-07
  • 2017-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-27
  • 2014-05-21
相关资源
最近更新 更多