首页 > 解决方案 > 我在将数据从 XML 文件移动到具有 CDATA 节点类型的 ARRAY 时遇到问题

问题描述

根据标题,从带有CDATA元素的 XML 文件获取数据到数组中时遇到问题。基于我目前对如何做的有限理解,我想出了这个基本的工作方法 CDATA很奇怪,所以我的正常方法不起作用。我找到节点的正常路线并没有停在它们上面,然后就是整个CDATA问题。

XmlTextReader xmlReader = new XmlTextReader(FilePath);
while (xmlReader.Read())
{
    // Position the reader on the OrderNumber node
    xmlReader.ReadToFollowing("quoteNumber");
    XmlReader inner = xmlReader.ReadSubtree();
    while (inner.Read())
    {
        switch (xmlReader.NodeType)
        {
            case XmlNodeType.CDATA:
                Globals.COData[0] = inner.Value;
                break;
        }
    }

    xmlReader.ReadToFollowing("orderNumber");
    inner = xmlReader.ReadSubtree();
    while (inner.Read())
    {
        switch (xmlReader.NodeType)
        {
            case XmlNodeType.CDATA:
                Globals.COData[1] = inner.Value;
                break;
        }
    }

但是我有很多数据元素要获取并假设有更好的方法。文件看起来像:

XML 图像

以及相关部分:

<quoteNumber>
<![CDATA[ John Test 123]]>
</quoteNumber>
<orderNumber>
<![CDATA[ 1352738]]> 
</orderNumber>

包含的项目在文件末尾确实有一个结束元素。整个 XML 太大而无法发布。

XML 格式不在我的控制范围内。

我的最终目标是将OrderNumber其及其值放入一个数组中。及其Quote number价值。我习惯于看到<OrderNumber>123</OrderNumber>这样CDATA的节点对我来说是新的。

标签: c#.netxmlxmlreadercdata

解决方案


由于您没有共享完整的 XML,因此并不完全清楚您哪里出错了,但是您没有从循环XmlReader.ReadToFollowing(string)内部检查返回值。Read()因此,一旦你读过最后一个,当没有找到<orderNumber>另一个时你会得到一个异常。<quoteNumber>

我建议重组你的代码如下:

var ns = ""; // Replace with @"http://intelliquip.com/integrationS..." can't see the full namespace from the XML image.
var list = new List<Tuple<string, string>>(); // List of (quoteNumber, orderNumber) values.
var xmlReader = XmlReader.Create(FilePath);
while (xmlReader.ReadToFollowing("quoteNumber", ns))
{
    string quoteNumber = null;
    string orderNumber = null;
    using (var inner = xmlReader.ReadSubtree())
    {
        // We need to skip the insignificant whitespace around the CDATA nodes which ReadElementContentAsString() will not do.
        while (inner.Read())
        {
            switch (xmlReader.NodeType)
            {
                case XmlNodeType.Text:
                case XmlNodeType.CDATA:
                    quoteNumber += inner.Value;
                    break;
            }
        }
        // After ReadSubtree() the reader is positioned on the </quoteNumber> element end.
    }
    // If the next orderNumber node is nmissing, ReadToFollowing() will read all the way past the next quoteNumber node.  
    // Use ReadToNextSibling() instead.
    if (xmlReader.ReadToNextSibling("orderNumber", ns))
    {
        using (var inner = xmlReader.ReadSubtree())
        {
            while (inner.Read())
            {
                switch (xmlReader.NodeType)
                {
                    case XmlNodeType.Text:
                    case XmlNodeType.CDATA:
                        orderNumber += inner.Value;
                        break;
                }
            }
        }
    }

    if (quoteNumber != null && orderNumber != null)
        list.Add(Tuple.Create(quoteNumber, orderNumber)); 
    else
    {
        // Add error handling here
    }
}

笔记:

  • CDATA只是编码 XML 字符数据节点的另一种方法,请参阅 XML中的<![CDATA[]]> 是什么意思?详情。XmlReader.Value将包含 XML 字符数据节点的未转义值,无论它是编码为常规文本节点还是CDATA节点。

  • 从您的问题中不清楚 XML 文件中是否必须只有一个<quoteNumber>节点。因此,我将报价和订单号对读入List<Tuple<string, string>>. 阅读完成后,您可以检查已阅读的数量,然后Globals.COData酌情添加。

  • XmlReader.ReadToFollowing()返回

    true如果找到匹配元素;否则false处于XmlReader文件结束状态。

    因此,需要检查它的返回值,以确保您不会尝试读取文件末尾。

  • 您的代码不会尝试处理<orderNumber>缺少 an 的情况。如果是,则代码可能会一直跳过下一个<quoteNumber>以读取其订单号。为了避免这种可能性,我使用XmlReader.ReadToNextSibling()将搜索范围限制为<orderNumber>属于同一父节点的节点。

  • 通过使用XmlReader.ReadToFollowing("orderNumber")硬编码代码来假设orderNumber节点没有命名空间前缀。与其这样做,不如明确指出它们所在的名称空间,这似乎类似于未显示http://intelliquip.com/integrationS... 该部分的位置。...

    我建议使用XmlReader.ReadToFollowing("orderNumber", ns)wherens是 order 和 quote 节点实际所在的命名空间。

  • XmlTextReader自 .Net 2.0 以来已被弃用。改为使用XmlReader.Create()

  • API使用XmlReader起来相当繁琐。如果您的 XML 文件不大,您可以考虑将它们加载到LINQ to XML 中XDocument并使用LINQ to XML来查询它。

    例如,您的XmlReader代码可以重写如下:

     var doc = XDocument.Load(FilePath);
     XNamespace ns = ""; // Replace with @"http://intelliquip.com/integrationS..." can't see the full namespace from the XML image.
     var query = from quote in doc.Descendants(ns + "quoteNumber")
         let order = quote.ElementsAfterSelf(ns + "orderNumber").FirstOrDefault()
         where order != null
         select Tuple.Create(quote.Value, order.Value);
    
     var list = query.ToList();
    

    这看起来要简单得多。

  • 您也可以考虑将 替换Tuple<string, string>为适当的数据模型,例如

    public class Order
    {
        public string QuoteNumber { get; set; }
        public string OrderNumber { get; set; }
    }
    

Demo fiddle #1 here for XmlReaderand #2 here for LINQ to XML。


推荐阅读