解析带有内部节点的XML?

4

我有一个像下面这样的XML文件:

 <clients>
  <client>
    <id>YYYY</id>
    <name>XXXX</name>
    <desc>ZZZZ</desc>
    <trade_info>
      <tab_list>
       <data>
         <tab>book 123</tab>
       </data>
       <data>
         <tab>cook 321</tab>
       </data>
      </tab_list>
      <buy_price_rate>200</buy_price_rate>
    </trade_info>
  </client>
 </clients>

我需要从中提取id、name、desc,并且从内部节点trade_info中提取data/tab、buy_price_rate

所以一开始我的想法是这样的:

    var query = from node in doc.Descendants("client")
                select new
                {
                    client = new
                    {
                        Id = node.Element("id").Value,
                        Name = node.Element("name").Value,
                        Desc = node.Element("desc").Value
                    },

                    trade = from n in node.Descendants("trade_info")
                               select new
                               {
                                   Id = n.Element("tab_list").Element("data").Element("tab").Value,
                                   Buy = n.Element("buy_price_rate").Value
                               }
                };

        foreach (var item in query)
        {
            writeXML.WriteStartElement("tradelist_template");
            writeXML.WriteAttributeString("client_id", item.client.Id);
            foreach (var trade in item.trade)
            {
                writeXML.WriteStartElement("tradelist");
                writeXML.WriteAttributeString("item_id", trade.Id);
                writeXML.WriteEndElement();
            }
            writeXML.WriteEndElement();
        }

但是它似乎不起作用,我不确定如何进行调试。

从我得到的第一个错误中,空指针异常,我认为它可能来自于node.Descendants("trade_info"),因为有些客户根本没有交易信息。

我还认为有一些问题来自于:

Id = n.Element("tab_list").Element("data").Element("tab").Value,
Buy = n.Element("buy_price_rate").Value

有时他们的列表上没有物品或购买价格比率。

  • 如何在我的查询中检查它是否为null以确保安全?
  • 我的查询对于我想要的是否可以?
  • 我应该改变什么?建议?
3个回答

3
你可以这样做……
  var list = from item in doc.Descendants("client")
             let tradeinfoelement = item.Element("trade_info")
             select new
             {
               Client = new
               {
                 Id = (string)item.Element("id"),
                 Name = (string)item.Element("name"),
                 Desc = (string)item.Element("desc")
               },
               TradeInfo = new
               {
                 BuyPrice = tradeinfoelement.Element("buy_price_rate")  != null ? (int?)tradeinfoelement.Element("buy_price_rate") : null,
                 Tabs = tradeinfoelement.Descendants("tab") != null ? tradeinfoelement.Descendants("tab").Select(t => (string)t).ToList() : null
               }
             };

重要的是要确定你的包装类是什么样子,并确定在没有特定属性的数据时应该使用哪些默认值进行映射。(在这个例子中我选择了null)


2

您的值文档应该是XElement类型,然后您可以选择像id这样的内容。

var query = from el in doc.Descendants(XName.Get("id"))
 select el.Value;

使用XName并不是必须的,你可以直接使用字符串,但如果你的XML中的某些元素有命名空间,它仍然很有用。


我的文档是 XDocument doc = XDocument.Load(file);,你的回复对我来说有点含糊不清,但还是谢谢。 - Prix
如果您发布代码或XML,请在文本编辑器中突出显示这些行,并单击编辑器工具栏上的“代码”按钮(101 010)以使其格式化和语法高亮! - marc_s

2

您能将查询中的trade部分更改为以下内容吗:

trade = from n in node.Descendants("trade_info")
  select new
  {
    Id = (n.XPathSelectElement("tab_list/data/tab") == null) ? null : n.XPathSelectElement("tab_list/data/tab").Value,
    Buy = (n.Element("buy_price_rate") == null) ? null : n.Element("buy_price_rate").Value
  }

(你需要添加 using System.Xml.XPath)


只使用空值验证而不使用XPath,它运行得很好,但是使用XPath似乎更容易。谢谢。 - Prix
使用类似 n.Element("tab_list").Element("data").Element("tab").Value 的结构,我认为在每一步检查 null 值会很麻烦,使用 XPath 更容易。 - Carson63000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接