1

I am deserializing xml to objects with this method:

public T Deserialize(string filename)
{
    var xml = File.ReadAllText(filename);
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(xml);
    writer.Flush();
    stream.Position = 0;
    DataContractSerializer dcs = new DataContractSerializer(typeof(T));
    T obj = (T)dcs.ReadObject(stream);
    return obj;
}

I have an old XML, and since then some properties added/removed from the a class I am serializing/deserializing.

I have the following exception:

Deserialized object with reference id 'i53' not found in stream.

Is it possible to customize the DataContractSerializer to just skip a property if it is not in the model anymore? Note that the deleted property is a reference to another complex object, not to simple type. The XML file contains that, my class not anymore.

dbc
  • 104,963
  • 20
  • 228
  • 340
dvjanm
  • 2,351
  • 1
  • 28
  • 42
  • Possible duplicate of [How can I ignore a property when serializing using the DataContractSerializer?](https://stackoverflow.com/questions/1791946/how-can-i-ignore-a-property-when-serializing-using-the-datacontractserializer) – Wim Ombelets Jul 10 '17 at 09:34
  • @WimOmbelets Please read both questions. They are two different topic... – dvjanm Jul 10 '17 at 09:40
  • We need to see a [mcve] of your problem. It sounds like something is going wrong with [object reference tracking](https://stackoverflow.com/questions/1037201/isreference-property-in-data-contract). For instance, possibly you obsoleted a data member where an object is defined, so a later reference to the object in the XML via a `"z:Ref"` attribute fails. – dbc Jul 10 '17 at 09:49
  • @dbc Your are right, I did not mention in the question, that the deleted property is a reference to another complex object, not to simple type. The XML file contains that, my class not anymore. – dvjanm Jul 10 '17 at 09:57
  • Making a property private in a class will exclude property from xml. But you are getting an error when deserializing. You error is occurring because the xml contains a property that is not in the class. So if some of your xml files contain a tag then it must be in a class. – jdweng Jul 10 '17 at 10:24

1 Answers1

1

The exception message Deserialized object with reference id 'i53' not found in stream can get thrown when you enable the data contract serializer's object reference preservation functionality. It indicates that, during deserialization, a reference to an undefined object was encountered, and so cannot be deserialized.

I was able to reproduce the problem by obsoleting a data member as follows. First, I defined the following types:

namespace V1
{
    [DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)]
    public class Member
    {
        [DataMember]
        public string Name { get; set; }
    }

    [DataContract(Name = "Root", Namespace = "Question45008433")]
    public class RootObject
    {
        [DataMember(Order = 1)]
        public Member MainMember { get; set; }

        [DataMember(Order = 2)]
        public List<Member> Members { get; set; }
    }
}

Then I created a test object as follows:

var list = new List<V1.Member> { new V1.Member { Name = "Foo" }, new V1.Member { Name = "Bar" } };
var v1 = new V1.RootObject { MainMember = list[0], Members = list };

Notice that the Foo object is referred to twice, once from MainMember and once from the Members list.

When I serialized this using DataContractSerializer, I got the following XML:

<Root xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="Question45008433">
  <MainMember z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
    <Name>Foo</Name>
  </MainMember>
  <Members>
    <Member z:Ref="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
    <Member z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
      <Name>Bar</Name>
    </Member>
  </Members>
</Root>

Notice that the Foo object is fully serialized when it is first serialized as <MainMember>, whereupon it is given a z:Id="i1" attribute. When subsequent references are encountered during serialization, only a reference is serialized via z:Ref="i1".

Next, I decided that the MainMember data member was unnecessary and obsoleted it:

namespace V2
{
    [DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)]
    public class Member
    {
        [DataMember]
        public string Name { get; set; }
    }

    [DataContract(Name = "Root", Namespace = "Question45008433")]
    public class RootObject
    {
        [DataMember(Order = 2)]
        public List<Member> Members { get; set; }
    }
}

Now, if I try to deserialize the original XML using this modified contract, I get the very exception you are seeing:

System.Runtime.Serialization.SerializationException: Deserialized object with reference id 'i1' not found in stream.
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.GetExistingObject(String id, Type type, String name, String ns)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, String name, String ns, Object& retObj)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract)

Why is this happening? It occurs because the obsoleted data member came before the remaining data members. Thus, during deserialization, the defining element is skipped and ignored, and the subsequent references cannot get resolved.

The workaround is to add back the original data member as a private fake synthetic property that does nothing and always returns null:

namespace V3
{
    [DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)]
    public class Member
    {
        [DataMember]
        public string Name { get; set; }
    }

    [DataContract(Name = "Root", Namespace = "Question45008433")]
    public class RootObject
    {
        [DataMember(EmitDefaultValue = false, Order = 1)]
        Member MainMember
        {
            get
            {
                return null;
            }
            set
            {
                // Do nothing
            }
        }


        [DataMember(Order = 2)]
        public List<Member> Members { get; set; }
    }
}

The original XML can now be deserialized successfully because, during deserialization, the data contract serializer itself maintains a lookup table of all reference elements by name. However, the z:Ref="i1" element only gets added when encountered if it corresponds to a currently valid member. And, because EmitDefaultValue = false, the obsoleted element will no longer appear when serialized.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • 1
    Thank you for this great effort, you perfectly reproduced and solved my problem that I did not even describe properly. – dvjanm Jul 10 '17 at 11:14
  • Is nt that we can skip if there is an extra element found while deserialization? I mean , we should be always able to deserialize to what we want even if there are extra members present in the serialized content right ? – Kumar Sep 30 '21 at 21:40