我在将数据从 XML 文件移动到具有 CDATA 节点类型的 ARRAY 时遇到问题

Posted

技术标签:

【中文标题】我在将数据从 XML 文件移动到具有 CDATA 节点类型的 ARRAY 时遇到问题【英文标题】:I am having trouble moving data from an XML file into an ARRAY with CDATA node type 【发布时间】:2021-06-18 16:49:10 【问题描述】:

根据标题,从带有 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;
        
    

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

以及相关部分:

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

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

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

我的最终目标是将OrderNumber 及其值放入一个数组中。还有Quote number 及其价值。我习惯于看到&lt;OrderNumber&gt;123&lt;/OrderNumber&gt; 所以CDATA 节点对我来说是新的。

【问题讨论】:

问题作者的附加评论:我的最终目标是将“OrderNumber”及其值放入数组中。以及“报价编号”及其值。我的问题不是很清楚。我习惯于看到 123,所以这对我来说是新的。 您的 XML 图像没有用处,只是表明您的元素位于某些未完全显示的根默认命名空间中。您是否有机会编辑您的问题以包含“原始”XML 作为文本而不进行任何转义或重新格式化?例如。在实际的 XML 中,&lt;![CDATA[...]]&gt; 注释被微不足道的空格包围,或者这是您如何格式化问题的产物? 【参考方案1】:

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

我建议按如下方式重构您的代码:

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 字符数据节点的另一种方式,请参阅 What does <![CDATA[]]> in XML mean? 了解详细信息。 XmlReader.Value 将包含 XML 字符数据节点的未转义值,无论它是编码为常规文本节点还是 CDATA 节点。

从您的问题中不清楚 XML 文件中是否必须恰好有一个 &lt;quoteNumber&gt; 节点。因此,我将报价和订单号对读入List&lt;Tuple&lt;string, string&gt;&gt;。阅读完成后,您可以检查阅读了多少,然后酌情添加到Globals.COData

XmlReader.ReadToFollowing() 返回

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

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

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

通过使用XmlReader.ReadToFollowing("orderNumber"),您可以对代码进行硬编码以假设orderNumber 节点没有命名空间前缀。与其这样做,不如明确指出它们所在的命名空间会更安全,其中似乎类似于http://intelliquip.com/integrationS...,其中未显示... 部分。

我建议使用XmlReader.ReadToFollowing("orderNumber", ns),其中ns 是订单和报价节点实际所在的命名空间。

XmlTextReader 自 .Net 2.0 起已被弃用。请改用XmlReader.Create()

XmlReader API 使用起来相当麻烦。如果您的 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&lt;string, string&gt; 替换为适当的数据模型,例如

public class Order

    public string QuoteNumber  get; set; 
    public string OrderNumber  get; set; 

演示小提琴 #1 here 用于 XmlReader 和 #2 here 用于 LINQ to XML。

【讨论】:

非常感谢,我会仔细研究建议,看看我能想出什么。我的代码很糟糕,永远不会发布到生产环境中——正在寻找方向。我将有大约 20 个元素要取出,然后还必须遍历订单项。 ReadSubtree() 在通过复杂的 XML 流式传输时非常有帮助,它保证你不会读得太少或太多。例如,如果您想搜索某个名称的元素,然后通过它们的子元素进行流式搜索,您可以使用this answer 中的XmlReaderExtensions.ReadAllSubtrees(this XmlReader reader, string localName, string namespaceURI) 来枚举它们。

以上是关于我在将数据从 XML 文件移动到具有 CDATA 节点类型的 ARRAY 时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

从多级 XML 结果 (API) 中提取 CDATA 时遇到问题

在 PHP 中修改 ![CDATA[]]? (XML)

为啥我在使用表情符号获取 cdata 元素时在 xml 标记中获取多个 cdata

使用 CDATA 解析 xml 响应的参数中的 html 数据

使用 xsl 时 CDATA 未包含在我的 XML 中

simpleXML 从 CDATA 获取值 [重复]