将 ISerializable 与 DataContractSerializer 一起使用时,如何阻止序列化程序输出类型信息?

Posted

技术标签:

【中文标题】将 ISerializable 与 DataContractSerializer 一起使用时,如何阻止序列化程序输出类型信息?【英文标题】:When using ISerializable with DataContractSerializer, how do I stop the serializer from outputting type information? 【发布时间】:2010-10-17 04:20:07 【问题描述】:

为了更好地控制序列化,我将一个类从[DataContract] 转换为[Serializable],同时实现了GetObjectData 和特殊的反序列化构造函数。当我这样做时,发出的 XML 现在具有应用于所有元素的类型信息。我不想要这些多余的信息,我想知道如何通知序列化器不要输出它。

这是使用[DataContract]的示例代码:

[DataContract(Namespace = "")]
class Test 

    public Test()  
    [DataMember]
    public Nullable<int> NullableNumber = 7;
    [DataMember]
    public int Number = 5;

    public static void Go()
    
        var test = new Test();
        var dcs = new DataContractSerializer(typeof(Test));
        using (var s = new StreamWriter("test.xml"))
        
            dcs.WriteObject(s.BaseStream, test);
        
            

这会输出以下 XML(注意没有关于 Nullable Number 和 Number 的类型信息——这是所需的输出):

<Test xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <NullableNumber>7</NullableNumber>
  <Number>5</Number>
</Test>

如果我把上面的代码修改成如下(添加[Serializable], :ISerializable, 和两个序列化方法):

[Serializable]
class Test : ISerializable

    public Test()  
    public Nullable<int> NullableNumber = 7;
    public int Number = 5;

    public static void Go()
    
        var test = new Test();
        var dcs = new DataContractSerializer(typeof(Test));
        using (var s = new StreamWriter("test.xml"))
        
            dcs.WriteObject(s.BaseStream, test);
        
            
    public Test(SerializationInfo info, StreamingContext context)
    
        NullableNumber = info.GetInt32("NullableNumber");
        Number = info.GetInt32("Number");
    

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    
        info.AddValue("NullableNumber", NullableNumber);
        info.AddValue("Number", Number);
    

它现在发出以下 XML。注意添加到每个元素的类型信息 (i:type="x:int")。

<Test xmlns="http://schemas.datacontract.org/2004/07/XMLSerialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
  <NullableNumber i:type="x:int" xmlns="">7</NullableNumber>
  <Number i:type="x:int" xmlns="">5</Number>
</Test>

为什么要这样做?如何阻止它这样做?

谢谢!

【问题讨论】:

感谢您的提问,因为它解决了我的问题 :-) 至于“为什么”——在第一个示例中,保证每个条目都是一个字段,因此您可以获得只需查看Test 类型的字段。在第二种情况下,处于控制之中,因此这些条目根本不必是字段,您可能只是在写入/读取随机数据。 现在有一个解决方案,可从 .NET Framework 4.5 获得。请参阅下面的答案 【参考方案1】:

你需要ISerializable吗?普通的DataContractSerializer 没有给你什么?如果你切换回这个,它应该可以正常工作。

基本上,通过实现自定义序列化,数据不再是基于契约的 - 所以它必须包含这些额外信息以保证它以后能够理解它。

那么:在这种情况下是否有理由实施ISerializable

【讨论】:

我精简了示例以使问题更容易。由于此处未显示的原因,我需要自定义序列化。 在我需要自定义序列化的(一长串)原因中,最大的原因是我需要根据其他信息有条件地输出某些属性。 我不明白您关于“它'必须'包含这些额外信息”的评论。实际上,上面的第一个 XML 示例使用 [Serializable] 反序列化器反序列化就好了,因此反序列化器不需要这种类型信息。【参考方案2】:

如果你想完全控制 xml 的序列化,你可以使用 XmlSerializer

public class Test

    [XmlIgnore]
    public Nullable<int> NullableNumber = 7;

    [XmlElement("NullableNumber")]
    public int NullableNumberValue
    
        get  return NullableNumber.Value; 
        set  NullableNumber = value; 
    

    public bool ShouldSerializeNullableNumberValue()
    
        return NullableNumber.HasValue;
    

    [XmlElement]
    public int Number = 5;

示例序列化代码:

static void Main(string[] args)

    XmlSerializer serializer = new XmlSerializer(typeof(Test));
    serializer.Serialize(Console.Out, new Test());

结果:

<Test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Number>5</Number>
  <NullableNumber>7</NullableNumber>
</Test>

【讨论】:

【参考方案3】:

从 .Net Framework 4.5(和 .Net Core 1.0)开始,可以使用 DataContractJsonSerializerSettings 类:

DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings

    EmitTypeInformation = EmitTypeInformation.Never
;
var dcs = new DataContractSerializer(typeof(Test), settings);

EmitTypeInformation 设置告诉序列化程序在序列化期间不要输出(烦人?)__type 参数。

还有一系列其他有用的设置可用。 Here is the docs page 为DataContractJsonSerializerSettings

【讨论】:

以上是关于将 ISerializable 与 DataContractSerializer 一起使用时,如何阻止序列化程序输出类型信息?的主要内容,如果未能解决你的问题,请参考以下文章

Serializable 指示一个类可以序列化;ICloneable支持克隆,即用与现有实例相同的值创建类的新实例(接口);ISerializable允许对象控制其自己的序列化和反序列化过程(接口)(

ISerializable 接口的意义何在?

[网络安全提高篇] 一一二.DataCon Coremail邮件安全竞赛之钓鱼邮件识别及分类

DataCon 2020:大数据安全分析名人堂的邀请函 请注意查收

具有 ISerializable 构造函数的可序列化类

ISerializable 对象应该如何存储二进制数据?