Populating custom subdirectory from XML document via LINQ

I figured out how to populate a custom class from XML data, but I ran into a problem along the way. Everything worked fine with my existing data fill method until I got thrown a little curve ball. The new schema I posted looks like this:

<ITEM_REPLY>
<TRAN_ID>1320691307345</TRAN_ID>
<REPLY_CODE>0</REPLY_CODE>
<UNIT_PRICE>8.2784</UNIT_PRICE>
<SUP_LOCS>
  <SUP_LOC>
     <SUP_LOC_ID>001134</SUP_LOC_ID>
     <COUNTRY_ID>USA</COUNTRY_ID>
     <QTY_AVL>47.000</QTY_AVL>
     <ITEM_UOM>EA</ITEM_UOM>
  </SUP_LOC>
  <SUP_LOC>
     <SUP_LOC_ID>006817</SUP_LOC_ID>
     <COUNTRY_ID>USA</COUNTRY_ID>
     <QTY_AVL>20.000</QTY_AVL>
     <ITEM_UOM>EA</ITEM_UOM>
  </SUP_LOC>
</SUP_LOCS>
<MESSAGE />
<QTY_BREAKS />
</ITEM_REPLY>

      

Pretty standard XML schema, the problem is I'm not sure how to populate my own class. Here's what I have:

static void Main(string[] args)
{
    var order = ConvertXMLMessage<ItemReply>(request);
}

protected static T ConvertXMLMessage<T>(String xmlData) where T : class, new()
{
    var xml = new XmlDocument();
    xml.LoadXml(xmlData);

    var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

    using (var xmlReader = new XmlNodeReader(xml.DocumentElement))
    {
        T work = (T)(serializer.Deserialize(xmlReader));
        return work;
    }
}

public class ItemReply
{
    [XmlElement("ITEM_REPLY")]
    public ItemAvlReply ITEM_REPLY { get; set; }
}
public class ItemAvlReply
{
    [XmlElement("TRAN_ID")]
    public string TRAN_ID { get; set; }
    [XmlElement("REPLY_CODE")]
    public string REPLY_CODE { get; set; }
    [XmlElement("UNIT_PRICE")]
    public string UNIT_PRICE { get; set; }
    [XmlElement("SUP_LOCS")]
    public SupplierLocations SUP_LOCS;
    [XmlElement("MESSAGE")]
    public string MESSAGE { get; set; }
    [XmlElement("QTY_BREAKS")]
    public string QTY_BREAKS { get; set; }
}
public class SupplierLocations
{
    [XmlElement("SUP_LOC")]
    public List<SupplierLocation> SUP_LOC;
}
public class SupplierLocation
{
    [XmlElement("SUP_LOC_ID")]
    public string SUP_LOC_ID { get; set; }
    [XmlElement("COUNTRY_ID")]
    public string COUNTRY_ID { get; set; }
    [XmlElement("QTY_AVL")]
    public string QTY_AVL { get; set; }
    [XmlElement("ITEM_UOM")]
    public string ITEM_UOM { get; set; }
}

      

This works fine minus part List<Item>

. I'm not too experienced with LINQ and I'm not sure how to do this by declaring an auxiliary array in my class using this statement. I'm also open to a different approach from creating a piece List<Item>

, I'm just not sure where to start otherwise. Is there a better approach to what I need to do? Is there a simple solution that I just don't know about LINQ?

+1


source to share


1 answer


Here's a simple way to do it, assuming the sample XML file you provided has typos. I assumed that OrderId has a closing tag and that there Items

should be a closing tag for /Items

.

Here's the xml version I used:

<Order>
<TransactionID>123</TransactionID>
<OrderID>1</OrderID>
<Items Number="2">
    <Item>
        <ItemName>Test</ItemName>
        <Color>Red</Color>
    </Item>
    <Item>
        <ItemName>Test1</ItemName>
        <Color>Blue</Color>
    </Item>
</Items>
</Order>

      

Here's the code to read / write XML: (xml variable is a string)

var order = ConvertXMLMessage<Order>(xml);
WriteXMLFile<Order>(order, @"test.xml");

      

Here's the ConvertXMLMessage and WriteXMLFile functions:

protected static T ConvertXMLMessage<T>(String xmlData) where T : class, new()
{
    var xml = new XmlDocument();
    xml.LoadXml(xmlData);

    var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

    using (var xmlReader = new XmlNodeReader(xml.DocumentElement))
    {
        T work = (T)(serializer.Deserialize(xmlReader));
        return work;
    }
}

protected static void WriteXMLFile<T>(T item, String saveLocation) where T : class, new()
{
    System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(typeof(T));

    System.IO.StreamWriter file = new System.IO.StreamWriter(saveLocation);
    writer.Serialize(file, item);
    file.Close();
}

      

and here's the class structure:



public class Order
{
    [XmlElement("TransactionID")]
    public string TransactionId { get; set; }
    [XmlElement("OrderID")]
    public string OrderId { get; set; }
    [XmlElement("Items")]
    public ItemsContainer Items;
}

public class ItemsContainer
{
    [XmlAttribute("Number")]
    public Int32 Number { get; set; }

    [XmlElement("Item")]
    public List<Item> Items;
}

public class Item
{
    [XmlElement("ItemName")]
    public string ItemName { get; set; }
    [XmlElement("Color")]
    public string Color { get; set; }
}

      

As you can see, I've added some attributes to let the XML parser know how to handle the class when converting it from / to XML. I also added another small class called "ItemsContainer" to hold the details in the Items tag. If you don't need the Number attribute, you can probably find a way to end this. However, this should get you in the right direction.

The example I gave is a simple version of how I usually handle the situation, obviously you can make some improvements depending on your needs.

Edit I changed the Item class to use ItemName

instead TransactionId

. This was my supervision.

Edit 2 This is where you need to fix the new code. The reason the class Order

worked in the previous example was for the XML root element. You are new XML, align with the base class. So we need to add a few more attributes to make this work. You can also remove the class ItemReply

. It is not necessary.

So here are the new classes:

[XmlRoot("ITEM_REPLY")]
public class ItemAvlReply
{
    [XmlElement("TRAN_ID")]
    public string TRAN_ID { get; set; }
    [XmlElement("REPLY_CODE")]
    public string REPLY_CODE { get; set; }
    [XmlElement("UNIT_PRICE")]
    public string UNIT_PRICE { get; set; }
    [XmlElement("SUP_LOCS")]
    public SupplierLocations SUP_LOCS;
    [XmlElement("MESSAGE")]
    public string MESSAGE { get; set; }
    [XmlElement("QTY_BREAKS")]
    public string QTY_BREAKS { get; set; }
}
public class SupplierLocations
{
    [XmlElement("SUP_LOC")]
    public List<SupplierLocation> SUP_LOC;
}
public class SupplierLocation
{
    [XmlElement("SUP_LOC_ID")]
    public string SUP_LOC_ID { get; set; }
    [XmlElement("COUNTRY_ID")]
    public string COUNTRY_ID { get; set; }
    [XmlElement("QTY_AVL")]
    public string QTY_AVL { get; set; }
    [XmlElement("ITEM_UOM")]
    public string ITEM_UOM { get; set; }
}

      

Everything else should remain the same. Parsing / transforming XML into classes should work without any changes.

+2


source







All Articles