Search code examples
c#.netxmlwcf

Deserialize XML in WCF with DataContractSerializer


I'm posting an XML document to a WCF REST service. I was able to get the elements to parse with the DataContractSerializer but I wasn't able to pull out the attributes. I've tried several methods from other SO question but I either couldn't get the attributes to parse or I couldn't get the correct objects built. For example my code at the moment only parses one FieldDatas and the Fields List is empty. The code provided is my latest attempt.

I implemented IXmlSerializable on my MLW class and was able to parse out the attributes, but I'm having trouble with the children. Because I'm using XMlReader, I get unexpected result because the readers in the child classes don't have any context with regard to their position in the document. Other examples show only one datatype in a container element that can be parsed to a List, but I have several FieldData elements that are siblings of the Job elements.

Is there a better way to accomplish this?

Here's my MLW class

[XmlRoot(ElementName = "MLW", Namespace = "")]
public class MLW : IXmlSerializable
{
    public string Cmd { get; set; }
    public string OrgId { get; set; }
    public string Id { get; set; }
    public string LTStamp { get; set; }
    public string TStamp { get; set; }
    public string DevId { get; set; }
    public string RouteId { get; set; }
    public string TruckId { get; set; }
    public string SType { get; set; }
    public string LocationAddress { get; set; }
    public string LocationCity { get; set; }
    public string LocationState { get; set; }
    public string LocationPostalCode { get; set; }
    public string StopId { get; set; }
    public string LocationKey { get; set; }

    public List<FieldData> FieldDatas = new List<FieldData>();

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(FieldData));

        Cmd = reader.GetAttribute("Cmd");
        OrgId = reader.GetAttribute("OrgId");
        Id = reader.GetAttribute("Id");
        LTStamp = reader.GetAttribute("LTStamp");
        TStamp = reader.GetAttribute("TStamp");
        DevId = reader.GetAttribute("DevId");
        RouteId = reader.GetAttribute("RouteId");
        TruckId = reader.GetAttribute("TruckId");
        SType = reader.GetAttribute("SType");
        LocationAddress = reader.GetAttribute("LocationAddress");
        LocationCity = reader.GetAttribute("LocationCity");
        LocationState = reader.GetAttribute("LocationState");
        LocationPostalCode = reader.GetAttribute("LocationPostalCode");
        StopId = reader.GetAttribute("StopId");
        LocationKey = reader.GetAttribute("LocationKey");

        while (reader.Read())
        {
            if (reader.IsStartElement())
            {
                switch (reader.Name.ToString())
                {
                    case "FieldData":
                        FieldDatas.Add((FieldData)serializer.Deserialize(reader));
                        break;
                }
            }
        }
    }

    public void WriteXml(XmlWriter writer){ }
}

My FieldData class:

public class FieldData : IXmlSerializable
{
    public string LCode { get; set; }
    public string OwnerId { get; set; }
    public List<Field> Fields = new List<Field>();

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Field));

        LCode = reader.GetAttribute("LCode");
        OwnerId = reader.GetAttribute("OwnerId");

        reader.ReadToDescendant("FieldData");

        while (reader.ReadToNextSibling("Field"))
        {
            Fields.Add((Field)serializer.Deserialize(reader));
        }
    }

    public void WriteXml(XmlWriter writer) { }
}

My Field class

public class Field : IXmlSerializable
{
    public string FId { get; set; }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        FId = reader.GetAttribute("FId");
    }

    public void WriteXml(XmlWriter writer) { }
}

And this is the XML

<?xml version="1.0" encoding="UTF-8"?>
<MLW Cmd="8" OrgId="999999944" Id="8888" LTStamp="2012-01-31T16:33:18" TStamp="2012-01-31T20:33:18" DevId="f7d4e3efbc9c555ef45f0a920d505c0c" RouteId="R1101-1" TruckId="50242" SType="3" LocationAddress="790 REDWOOD SQ,UNIT 3" LocationCity="OAKVILLE" LocationState="ON" LocationPostalCode="L6L6N3" StopId="43225" LocationKey="43225">
   <FieldData LCode="1" OwnerId="43225">
      <Field FId="1962" Name="Capture Signature?" Value="Signed">
         <Child FId="1963" Name="Received By" Value="JM" />
         <Child FId="1964" Name="Signature" Key="Signature" Value="abc123" />
      </Field>
      <Field FId="1543" Name="Disclaimer" Value="See Terms and Conditions at www.midlandtransport.com No claims will accepted unless noted on this device." />
      <RField>
         <Field FId="1538" Name="Delays" Value="Waiting For Door">
            <Child FId="1862" Name="Time Spent" Value="15 min" />
         </Field>
         <Field FId="1538" Name="Delays" Value="Waiting to get Unloaded">
            <Child FId="1862" Name="Time Spent" Value="30 min" />
         </Field>
      </RField>
   </FieldData>
   <FieldData LCode="2" OwnerId="Order1100-2">
      <RField>
         <Field FId="1533" Name="Accessorials" Value="Tail Gate" />
         <Field FId="1533" Name="Accessorials" Value="2 Man Service" />
      </RField>
      <Field FId="2106" Name="Driver Collect?" Value="True">
         <Child FId="2109" Name="Driver Coll Amt" Value="32" />
      </Field>
      <Field FId="2065" Name="COD?" Value="True">
         <Child FId="2066" Name="COD Amt" Value="45" />
      </Field>
   </FieldData>
   <FieldData LCode="2" OwnerId="Order1100-4">
      <RField>
         <Field FId="1533" Name="Accessorials" Value="Tail Gate" />
         <Field FId="1533" Name="Accessorials" Value="2 Man Service" />
      </RField>
      <RField>
         <Field FId="1516" Name="OSD Reason" Value="Damaged and Kept">
            <Child FId="1957" Name="Pieces" Value="55" />
            <Child FId="1960" Name="Units" Value="Roll" />
         </Field>
         <Field FId="1516" Name="OSD Reason" Value="Shortage (Concealed)">
            <Child FId="1957" Name="Pieces" Value="5" />
            <Child FId="1960" Name="Units" Value="Skid" />
         </Field>
         <Field FId="1516" Name="OSD Reason" Value="Refusal">
            <Child FId="1957" Name="Pieces" Value="500" />
            <Child FId="1960" Name="Units" Value="Unit" />
         </Field>
      </RField>
      <Field FId="2106" Name="Driver Collect?" Value="False" />
      <Field FId="2065" Name="COD?" Value="False" />
   </FieldData>
   <Job JobId="Order1100-2" JType="3" Status="4" DispatchStopId="396934">
      <Item ItemId="1" Status="4" Key="" Name="" />
   </Job>
   <Job JobId="Order1100-4" JType="3" Status="4" DispatchStopId="396936">
      <Item ItemId="1" Status="4" Key="" Name="" />
   </Job>
</MLW>

Update I was able to get it working by adding [XmlSerializerFormat] to the endpoint definition in the service interface and by using Visual Studio's Paste XML as Classes feature which builds the required classes for the XML for you.


Solution

  • You should try explicitly marking your properties as Elements or attributes.

    As in

    [XmlAttribute(AttributeName = "Cmd")]
    public string Cmd { get; set; }
    public string OrgId { get; set; }
    public string Id { get; set; }
    

    It also looks like you're doing way more than is necessary here. (I say that without knowing the broader context of your project though).

    You should be able to just create some pocos and let the datacontract serializer do its thing. Just use [XmlRoot], [XmlElement], [XmlAttribute], and [XmlArray] as needed to hint the serializer. I normally don't mess with around with all those readxml methods, as that basically amounts to manual deserialization, which isn't really saving me any time.