tags:

views:

1998

answers:

2

I have a WCF service which accepts a string as a paramter for one of its operation contracts. This string however has xml content in it.

I need to convert this into a class that is marked as a DataContract but is not exposed to the outside world.

I need to use the DataContractSerializer because the class members have the [DataMember] attribute set to a different name. Eg: the property Phone has the DataMember Name set as "Telephone", so when i deserialize the xmldocument using the normal serializer, i get an error as the deserializer looks for the Phone element which does not exist.

How do i de-serialize an XmlDocument using the DataContractSerializer? One constraint though is i cannot save the xmldocument to a file.

EDIT: Found an excellent article on serialization and de-serialization using DataContractSerializer here.

My client code:

            string xmldata = "<Customer>
+ System.Environment.NewLine+ 
            "<Age>1</Age>"+System.Environment.NewLine+
            "<BirthDate>1900-01-01T01:01:01.0000000-05:00</BirthDate>" + System.Environment.NewLine+
            "<FistName>John</FistName>"+ System.Environment.NewLine +
            "<LastName>Doe</LastName>"+System.Environment.NewLine +
            "</Customer>";

            doc.LoadXml(xmldata); 
            Service1Client a = new Service1Client();
            a.GetData(doc.OuterXml.ToString());

My service code:

        public string GetData(string per)
        {
            string xmldata = per;
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmldata);
            XmlDemo.Person a = Person.Create();

            DataContractSerializer ser = new DataContractSerializer(a.GetType());
            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter);
            xmlDoc.WriteTo(xmlWriter);

            MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes(stringWriter.ToString()));
            stream.Position = 0;
            XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
            Person myContact = (Person)ser.ReadObject(reader, true);

            return string.Empty; 

        }

My DataContract:

    [Serializable]
    [DataContract(Name = "Customer")]
    public class Person
    {
        private Person() {}
        [DataMember(Name = "FistName")]
        public string FName { get; set; }
        [DataMember(Name = "LastName")]
        public string LName { get; set; }
        [DataMember(Name = "Age")]
        public int Age { get; set; }
        [DataMember(Name = "BirthDate")]
        public DateTime DOB { get; set; }

        public static Person Create()
        {
            return new Person();
        }
    }

I get this error at Person myContact = (Person)ser.ReadObject(reader, true);

Error in line 1 position 11. Expecting element 'Customer' from namespace 'http://schemas.datacontract.org/2004/07/XmlDemo'.. Encountered 'Element'  with name 'Customer', namespace ''.
+1  A: 

Deserialize from string straight way

    MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes("<myXml />"));
    stream.Position = 0;
    XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
    MyContact myContact = (MyContact)ser.ReadObject(reader, true);

Deserialize from XmlDocument

   StringWriter stringWriter = new StringWriter();
    XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter);
    xmlDoc.WriteTo(xmlWriter);

    MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes(stringWriter.ToString()));
    stream.Position = 0;
    XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
    MyContact myContact = (MyContact)ser.ReadObject(reader, true);
codemeit
I tried your approach, i have listed my complete code and the error message i get when i try it with your code.
Nick
obviously, you declared http://schemas.datacontract.org/2004/07/XmlDemo namespace somewhere in your WCF service, in that case, you need to take care the namespace for the deserializer.
codemeit
Thank you!!! I put in [Namespace=""] for the data contract and it works now.
Nick
+1  A: 

Is this your service? If so, then don't send or receive XML in a string parameter, since they're not the same thing.

Also, though you haven't exposed the DataContract into which you want to deserialize this XML to the world, you pretty much have done so by exposing the XML. It's largely the same effect. So you should instead create a DataContract class that has no behavior associated with it, then expose that in your ServiceContract. If you need to use it internally as data in a class with behavior, then copy the data into a new instance of your internal class, that you never expose.


I still recommend you stop this silliness with copying XML into and out of strings. Still, based on the code and exception you posted:

Why do this

XmlDemo.Person a = Person.Create();

and then wipe it out when you deserialize?

XmlDemo.Person a;

is adequate. Then use typeof(XmlDemo.Person) instead of a.GetType().

Also, is the Person class you posted the exact same "XmlDemo.Person" class? I'm very curious to know where that namespace comes from. To remove my uncertainty, try changing the [DataContract] attribute to say Namespace=String.Empty (or maybe Namespace=null).

Finally, have you ever heard the one about the developer who didn't call Dispose on instances of classes that implement IDisposable?

John Saunders
The code i posted is a mishmash of different things i was trying to do. The factory method does not belong there. I guess i should have cleaned up the code before posting it :) I do agree with not passing xml as string, but this design has been handed down by the "architect" of the app...
Nick
Thank You John, you were right about the namespace, unfortunately SO does not allow me to mark multiple responses as answers.
Nick