Search code examples
xsddeserializationxmlserializerdatacontractserializerxsd2code

DataContractSerializer.ReadObject() fails to create object when used with SvcUtils.exe


I ran SvcUtil.exe against XSD file to generate class. Then tried to create Object out of XML using following line. I get error shown below. Please see the detailed code below.

PersonType prs = (PersonType)xs.ReadObject(new MemoryStream(File.ReadAllBytes(sFileName)));

Error in line 3 position 58. Expecting element 'PersonType' from namespace 'http://service.a1.com/base1/2005/'.. Encountered 'Element'  with name 'Person', namespace 'http://service.a1.com/base1/2005/'. 

Command Used

svcutil.exe" "C:\Temp\S1\UseXSDExe\UseXSDExe\Sample2\Prs.xsd" /t:code /language:cs /out:C:\SPrxy.cs /dconly

Full code

(class generated by SvcUtils.exe)
    [assembly: System.Runtime.Serialization.ContractNamespaceAttribute("http://service.a1.com/base1/2005/", ClrNamespace="service.a1.com.base1._2005")]
    namespace service.a1.com.base1._2005
    {
        using System.Runtime.Serialization;


        [System.Diagnostics.DebuggerStepThroughAttribute()]
        [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
        [System.Runtime.Serialization.DataContractAttribute(Name="PersonType", Namespace="http://service.a1.com/base1/2005/")]
        public partial class PersonType : object, System.Runtime.Serialization.IExtensibleDataObject
        {
            private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

            private string LastNameField;

            private string FirstNameField;

            public System.Runtime.Serialization.ExtensionDataObject ExtensionData
            {
                get
                {
                    return this.extensionDataField;
                }
                set
                {
                    this.extensionDataField = value;
                }
            }

            [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, EmitDefaultValue=false)]
            public string LastName
            {
                get
                {
                    return this.LastNameField;
                }
                set
                {
                    this.LastNameField = value;
                }
            }

            [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=1)]
            public string FirstName
            {
                get
                {
                    return this.FirstNameField;
                }
                set
                {
                    this.FirstNameField = value;
                }
            }
        }
    }

(code used for converting XML to object)
    public static void convertToObject(string sFileName)
    {
        DataContractSerializer xs = new DataContractSerializer(typeof(PersonType));
        PersonType Person = (PersonType)xs.ReadObject(new MemoryStream(File.ReadAllBytes(sFileName)));

    }

(XSD)

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://service.a1.com/base1/2005/"  xmlns:bse1="http://service.a1.com/base1/2005/" elementFormDefault="qualified">
        <xs:complexType  name="PersonType">
            <xs:sequence>
                <xs:element minOccurs="1" maxOccurs="1" name="LastName" type="xs:string" />
                <xs:element minOccurs="0" maxOccurs="1" name="FirstName" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
        <xs:element name="Person" type="bse1:PersonType"/> 
    </xs:schema>

(XML)

    <?xml version="1.0" encoding="utf-8"?>
    <pr:Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                xsi:schemaLocation="http://service.a1.com/base1/2005/ Prs.xsd" 
                xmlns:pr="http://service.a1.com/base1/2005/"> 
        <pr:LastName>   Lane </pr:LastName>
        <pr:FirstName>  Fane </pr:FirstName>
    </pr:Person>

I ran XSD.exe on same XSD file. Then I was able to convert XML to Object using XmlSerializer.Deserialize().

XSD does not have any attribute. I have validated XML against XSD.

Please let me know why Deserialize() fails.


Solution

  • Your XSD specifies a root element name and data type name that are different:

     <xs:element name="Person" type="bse1:PersonType"/> 
    

    When svcutil.exe generates data contract classes for this type, it puts the type name into the data contract rather than the root element name. This appears to be intentional, see Svcutil generates wrong Name property value in DataContractAttribute. Perhaps it does this since the contract type itself could be re-used anywhere in the object graph, and there's no data contract equivalent of XmlRoot that applies only when the type in question is the document's root element.

    As a workaround, you have a couple options:

    1. Hardcode the expected root element name when constructing the serializer:

          var xs = new DataContractSerializer(typeof(service.a1.com.base1._2005.PersonType), "Person", "http://service.a1.com/base1/2005/");
      
    2. Preload the XML into an XDocument and use the actual root element name when constructing the serializer:

          var doc = XDocument.Load(sFileName);
          service.a1.com.base1._2005.PersonType person;
          var xs = new DataContractSerializer(typeof(service.a1.com.base1._2005.PersonType), doc.Root.Name.LocalName, "http://service.a1.com/base1/2005/");
          using (var reader = doc.CreateReader())
          {
              person = (service.a1.com.base1._2005.PersonType)xs.ReadObject(reader);
          }
      
    3. Or use XmlSerializer.