16

To get more control over serialization, I have converted a class from [DataContract] to [Serializable], implementing both GetObjectData and the special deserializing constructor. When I do this, the XML emitted now has type information applied to all elements. I don't want this superfluous information, and I'm wondering how to inform the serializer to not output it.

Here's the sample code that uses [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);
        }
    }        
}

This outputs the following XML (notice no type info on Nullable Number and Number--this is the desired output):

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

If I modify the above code as follows (adding [Serializable], : ISerializable, and the two serialization methods):

[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);
    }
}

It now emits the following XML. Notice the type information (i:type="x:int") added to each element.

<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>

Why is it doing this? How do I stop it from doing it?

Thanks!

Yennefer
  • 5,704
  • 7
  • 31
  • 44
Eric
  • 2,029
  • 2
  • 26
  • 36
  • Thank you for the question, because it solved my question :-) As for "why" -- in the first example there was a guarantee each entry is a field, so you can get the type of the field just by looking at `Test` type. In second case **you** are in control, so those entries do not have to be fields at all, you could be writing/reading just random data. – astrowalker Jan 21 '17 at 09:22
  • There is now a solution to this, available from .NET Framework 4.5. See my answer below – Martin Jan 06 '20 at 20:33

3 Answers3

1

If you want full control over serialization to xml, you can use 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;
}

sample serialization code:

static void Main(string[] args)
{
    XmlSerializer serializer = new XmlSerializer(typeof(Test));
    serializer.Serialize(Console.Out, new Test());
}

results:

<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>
Dmitry Kolchev
  • 2,116
  • 14
  • 16
1

Starting in .Net Framework 4.5 (and .Net Core 1.0), this is possible using the DataContractJsonSerializerSettings class:

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

The EmitTypeInformation settings tells the serializer not to output the (annoying?) __type parameter during serialization.

There are a range of other useful settings available. Here is the docs page for DataContractJsonSerializerSettings.

Martin
  • 16,093
  • 1
  • 29
  • 48
0

Do you need the ISerializable here? What was the regular DataContractSerializer not giving you? If you switch back to this, it should work fine.

Basically, by implementing custom serialization, the data is no longer contract based - so it has to include this extra information to guarantee that it is able to understand it later.

So: is there a reason to implement ISerializable in this case?

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I trimmed down the example to make the question easier. I need custom serialization for reasons I don't show here. – Eric Apr 03 '09 at 00:10
  • Of the (long list) of reasons I need custom serialization, the biggest is that I need to conditionally output certain properties based on other information. – Eric Apr 03 '09 at 00:12
  • 1
    I don't understand your comment about "it 'has' to include this extra information". Indeed, the first XML example above deserializes just fine with the [Serializable] deserializer, so the deserializer does not need this type info. – Eric Apr 03 '09 at 00:15