Заполнить пользовательский подкласс List из XML-документа через LINQ

Я выяснил, как заполнить пользовательский класс из данных XML, но столкнулся с проблемой на этом пути. Все отлично работало с моим существующим методом заполнения данных до тех пор, пока меня не бросил немного кривая. Новая схема, которую мне прислали, похожа на эту:

<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>

Довольно стандартная XML-схема, проблема в том, что я не уверен, как заполнить ее собственным классом. Вот что у меня есть:

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; }
}

Это прекрасно работает минус List<Item> часть. Я не слишком опытен с LINQ, и я не уверен, как сделать объявление подмассива в моем классе с помощью этого оператора. Я также открыт для другого подхода от создания List<Item> часть, я просто не уверен, с чего начать иначе. Есть ли лучший подход для того, что мне нужно сделать? Есть ли простое решение, о котором я просто не знаю в LINQ?

1 ответ

Решение

Вот простой способ сделать это, предполагая, что у предоставленного вами примера XML-файла есть опечатки. Я предположил, что OrderId имеет закрывающий тег, и что закрывающий тег для Items должно быть /Items,

Вот версия xml, которую я использовал:

<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>

Вот код для чтения / записи XML: (переменная xml является строкой)

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

Вот функции ConvertXMLMessage и WriteXMLFile:

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();
}

и вот структура класса:

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; }
}

Как вы заметите, я добавил некоторые атрибуты, чтобы синтаксический анализатор XML знал, как обрабатывать класс при преобразовании из / в XML. Я также добавил еще один небольшой класс с именем "ItemsContainer", чтобы хранить подробности в теге Items. Если вам не нужен атрибут "Число", то вы, вероятно, можете найти способ покончить с этим. Тем не менее, это должно привести вас в правильном направлении.

Приведенный мною пример - это простая версия того, как я обычно справляюсь с ситуацией, очевидно, есть некоторые улучшения, которые вы можете сделать в зависимости от ваших потребностей.

Изменить Я изменил класс Item для использования ItemName вместо TransactionId, Это был недосмотр с моей стороны.

Изменить 2 Вот исправления, которые необходимо внести в недавно опубликованный код. Причина Order класс работал в предыдущем примере, если он соответствовал корневому элементу XML. Ваш новый XML соответствует базовому классу. Поэтому нам нужно добавить еще пару атрибутов, чтобы сделать эту работу. Вы также можете удалить свой ItemReply учебный класс. Это не нужно.

Итак, вот новые классы:

[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; }
}

Все остальное должно оставаться прежним. Анализ / преобразование XML в классы должно работать без каких-либо изменений.

Другие вопросы по тегам