在XmlDocument对象中,如何确保节点集合中的某个子节点存在且有值?
我正在维护一段代码,用于处理位于 System.Xml 命名空间中的 XmlDocument 类型对象。问题在于确保以下XML中的 <price> 元素既存在又有值。该元素或其值可能缺失的情况有多种:
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price></price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price />
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
<book id="bk104">
<author>Corets, Eva</author>
<title>Oberon's Legacy</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-03-10</publish_date>
<description>In post-apocalypse England, the mysterious
agent known only as Oberon helps to create a new life
for the inhabitants of London. Sequel to Maeve
Ascendant.</description>
</book>
</catalog>
我的目标是在缺失时添加 <price> 元素;或者如果它已经存在但没有值,则赋予一个值。
如果我按照 这篇帖子 的方法并使用如下方式:
Private Function EnsureValidPrice(xmlDoc As XmlDocument) As XmlDocument
Dim bookNodes = xmlDoc.SelectNodes("//book")
For Each book As XmlNode In bookNodes
Dim priceNode = book("price")
If priceNode Is Nothing OrElse String.IsNullOrWhiteSpace(priceNode.InnerText) Then
Dim ele As XmlNode = xmlDoc.CreateElement("price")
ele.InnerText = GetPrice(book.Attributes("id"))
book.AppendChild(ele)
End If
Next
Return xmlDoc
End Function
具有 id="bk101" 和 id="bk102" 的书籍的价格将仍然为空。
应该用一种简洁的方式覆盖这段XML中可能出现的条件?
解决方案
The price for the books with id="bk101" and id="bk102" will remain blank.
在这两种情况下,<price> 元素都存在,因此 priceNode Is Nothing 将返回False,接着 String.IsNullOrWhiteSpace(priceNode.InnerText) 将返回True,因此你会为 InnerText 赋值。
但是,你的代码的问题在于,当你对 InnerText 进行赋值时,你也在无条件地调用 CreateElement() 和 AppendChild(),因此你会在这些书中创建第二个 <price> 元素,然后为该新元素设置 InnerText,而不是对原本已经存在的元素进行设置。下次你再尝试读取这些书的价格时,会发现第一个空的 <price> 元素,而不是第二个已填充的 <price> 元素。
我会修改你的代码,先仅在确实缺失时创建一个空的 <price> 节点,然后再根据该元素的值判断它是已经存在还是新创建的,例如:
Private Function EnsureValidPrice(xmlDoc As XmlDocument) As XmlDocument
Dim bookNodes = xmlDoc.SelectNodes("//book")
For Each book As XmlNode In bookNodes
Dim priceNode as XmlNode = book("price")
If priceNode Is Nothing Then
priceNode = xmlDoc.CreateElement("price")
book.AppendChild(priceNode)
End If
If String.IsNullOrWhiteSpace(priceNode.InnerText) Then
priceNode.InnerText = GetPrice(book.Attributes("id"))
End If
Next
Return xmlDoc
End Function
更新:
或者,如果你需要保持更新后书籍的结构(即 <price> 插在 <genre> 和 <publish_date> 之间),请改用 InsertAfter()/InsertBefore() 而不是 AppendChild(),例如:
Private Function EnsureValidPrice(xmlDoc As XmlDocument) As XmlDocument
Dim bookNodes = xmlDoc.SelectNodes("//book")
For Each book As XmlNode In bookNodes
Dim priceNode as XmlNode = book("price")
If priceNode Is Nothing Then
priceNode = xmlDoc.CreateElement("price")
Dim refNode as XmlNode = book("genre")
If refNode IsNot Nothing Then
book.InsertAfter(priceNode, refNode)
Else
refNode = book("publish_date")
If refNode IsNot Nothing Then
book.InsertBefore(priceNode, refNode)
Else
book.AppendChild(priceNode)
End If
End If
End If
If String.IsNullOrWhiteSpace(priceNode.InnerText) Then
priceNode.InnerText = GetPrice(book.Attributes("id"))
End If
Next
Return xmlDoc
End Function