DataContractSerializer - change namespace and deserialize file bound to old namespace
Asked Answered
S

1

21

I have a data class that is serialized with the DataContractSerializer. The class uses the [DataContract] attribute with no explicit Namespace declaration. As such, the namespace in the resulting xml file is generated based on the namespace of the class.

The class basically looks like this:

namespace XYZ
{
   [DataContract]
   public class Data
   {
      [DataMember(Order = 1)]
      public string Prop1 { get; set; }

      [DataMember(Order = 2)]
      public int Prop2 { get; set; }
   }
}

...and the resulting xml:

<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/XYZ">
   <Prop1>StringValue</Prop1>
   <Prop2>11</Prop2>
</Data>

Now I want to change the namespace of the class (actually remove it) by changing the [DataContract] attribute to [DataContract(Namespace = "")]. However, once I do this any file previously serialized with the original namespace with no longer deserialize. I receive the following exception:

Error in line 1 position XXX. Expecting element 'Data' from namespace ''.. Encountered 'Element' with name 'Data', namespace 'http://schemas.datacontract.org/2004/07/XYZ'.

This makes perfect sense. I changed the namespace. I'm ok with that. However, it seems like there must be a way to tell the DataContractSerializer to go ahead and deserialize that data even though the namespaces don't match.

Skiplane answered 18/6, 2012 at 23:0 Comment(2)
Why do you want to specify an empty string for a namespace in the DataContract attribute? What are you gaining by doing that?Onomastic
I gain not being tied to the CLR namespace of the class; and that's really the problem here. This class is changing its CLR namespace and I don't want to be tied to that implementation detail. I could, of course, define an xml namespace value that I could keep constant. But for this implementation I don't see the benefit of doing that vs. simply removing the xml namespace. This is being used for simple file serialization.Skiplane
R
17

One possible way is to wrap the reader used by the serializer in a reader which maps the old namespace to the new one, as shown below. A lot of code, but mostly trivial.

public class StackOverflow_11092274
{
    const string XML = @"<?xml version=""1.0"" encoding=""utf-8""?> 
<Data xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/XYZ""> 
   <Prop1>StringValue</Prop1> 
   <Prop2>11</Prop2> 
</Data>";

    [DataContract(Name = "Data", Namespace = "")]
    public class Data
    {
        [DataMember(Order = 1)]
        public string Prop1 { get; set; }

        [DataMember(Order = 2)]
        public int Prop2 { get; set; }
    }

    public class MyReader : XmlReader
    {
        XmlReader inner;
        public MyReader(XmlReader inner)
        {
            this.inner = inner;
        }

        public override int AttributeCount
        {
            get { return inner.AttributeCount; }
        }

        public override string BaseURI
        {
            get { return inner.BaseURI; }
        }

        public override void Close()
        {
            inner.Close();
        }

        public override int Depth
        {
            get { return inner.Depth; }
        }

        public override bool EOF
        {
            get { return inner.EOF; }
        }

        public override string GetAttribute(int i)
        {
            return inner.GetAttribute(i);
        }

        public override string GetAttribute(string name, string namespaceURI)
        {
            return inner.GetAttribute(name, namespaceURI);
        }

        public override string GetAttribute(string name)
        {
            return inner.GetAttribute(name);
        }

        public override bool IsEmptyElement
        {
            get { return inner.IsEmptyElement; }
        }

        public override string LocalName
        {
            get { return inner.LocalName; }
        }

        public override string LookupNamespace(string prefix)
        {
            return inner.LookupNamespace(prefix);
        }

        public override bool MoveToAttribute(string name, string ns)
        {
            return inner.MoveToAttribute(name, ns);
        }

        public override bool MoveToAttribute(string name)
        {
            return inner.MoveToAttribute(name);
        }

        public override bool MoveToElement()
        {
            return inner.MoveToElement();
        }

        public override bool MoveToFirstAttribute()
        {
            return inner.MoveToFirstAttribute();
        }

        public override bool MoveToNextAttribute()
        {
            return inner.MoveToNextAttribute();
        }

        public override XmlNameTable NameTable
        {
            get { return inner.NameTable; }
        }

        public override string NamespaceURI
        {
            get
            {
                if (inner.NamespaceURI == "http://schemas.datacontract.org/2004/07/XYZ")
                {
                    return "";
                }
                else
                {
                    return inner.NamespaceURI;
                }
            }
        }

        public override XmlNodeType NodeType
        {
            get { return inner.NodeType; }
        }

        public override string Prefix
        {
            get { return inner.Prefix; }
        }

        public override bool Read()
        {
            return inner.Read();
        }

        public override bool ReadAttributeValue()
        {
            return inner.ReadAttributeValue();
        }

        public override ReadState ReadState
        {
            get { return inner.ReadState; }
        }

        public override void ResolveEntity()
        {
            inner.ResolveEntity();
        }

        public override string Value
        {
            get { return inner.Value; }
        }
    }

    public static void Test()
    {
        DataContractSerializer dcs = new DataContractSerializer(typeof(Data));
        MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(XML));
        try
        {
            XmlReader r = XmlReader.Create(ms);
            XmlReader my = new MyReader(r);
            Data d = (Data)dcs.ReadObject(my);
            Console.WriteLine("Data[Prop1={0},Prop2={1}]", d.Prop1, d.Prop2);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
}
Royalroyalist answered 19/6, 2012 at 6:49 Comment(2)
This worked great! I was attacking the problem at the wrong level. Thanks so much! I do have some more complex data types that contain Dictionary<TKey, TValue> instances that proved a bit more difficult. The tags for each KVP changed, I assume based on the namespace change, as such: <d2p1:KeyValueOfintDataLg3n2mfJ> to <d2p1:KeyValueOfintDataLjh4bohd>. I had to perform a manual conversion on these files but that's ok. These files/types represent a minority of what needs to be read.Skiplane
Oddly doesnt work for me. Get the same error as OP still, citing encountered namespace is "." Answer here says if xml is persisted then only hope is to adjust the attribute on the value class.Unconformity

© 2022 - 2024 — McMap. All rights reserved.