我在将数据从 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
及其价值。我习惯于看到<OrderNumber>123</OrderNumber>
所以CDATA
节点对我来说是新的。
【问题讨论】:
问题作者的附加评论:我的最终目标是将“OrderNumber”及其值放入数组中。以及“报价编号”及其值。我的问题不是很清楚。我习惯于看到<![CDATA[...]]>
注释被微不足道的空格包围,或者这是您如何格式化问题的产物?
【参考方案1】:
不完全清楚你哪里出错了,因为你没有共享完整的 XML,但是你没有从 Read()
循环内部检查来自 XmlReader.ReadToFollowing(string)
的返回值。因此,一旦你读过最后一个<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 字符数据节点的另一种方式,请参阅 What does <![CDATA[]]> in XML mean? 了解详细信息。 XmlReader.Value
将包含 XML 字符数据节点的未转义值,无论它是编码为常规文本节点还是 CDATA
节点。
从您的问题中不清楚 XML 文件中是否必须恰好有一个 <quoteNumber>
节点。因此,我将报价和订单号对读入List<Tuple<string, string>>
。阅读完成后,您可以检查阅读了多少,然后酌情添加到Globals.COData
。
XmlReader.ReadToFollowing()
返回
true
如果找到匹配元素;否则false
和XmlReader
处于文件结束状态。
因此需要检查它的返回值,以确保您不会尝试读取超过文件末尾的内容。
您的代码不会尝试处理缺少<orderNumber>
的情况。如果是,代码可能会一直跳过下一个<quoteNumber>
以读取其订单号。为了避免这种可能性,我使用XmlReader.ReadToNextSibling()
将搜索范围限制为属于同一父节点的<orderNumber>
节点。
通过使用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<string, string>
替换为适当的数据模型,例如
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 时遇到问题
为啥我在使用表情符号获取 cdata 元素时在 xml 标记中获取多个 cdata