通过LINQ从XML文档填充自定义List子类

时间:2014-11-06 16:48:35

标签: c# 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 个答案:

答案 0 :(得分:2)

这是一种简单的方法,假设您提供的示例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转换到XML时处理该类。我还添加了另一个名为&#34; ItemsContainer&#34;只是为了保存Items标签的细节。如果你不需要&#34; Number&#34;属性,然后你可能找到一种方法来消除这一点。但是,这应该会让你朝着正确的方向前进。

我提供的示例是我通常处理这种情况的简单版本,显然可以根据您的需要进行一些改进。

修改 我将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解析/转换为类应该无需任何更改。