[.NET]XML reader針對空節點的處理方式

[.NET]XML reader針對空節點的處理方式

在論談上有看到在問「XML reader "/>" 處理」,他使用以下的XML內容(有包含空節點,如Font & Line),如下,


   <Font Family="Verdana" Size="7" Color="DarkRed" /> 
   <Line Color="Red" /> 
</Root>

 

而程式是使用類似下面的程式(請先加入 using System.Xml, 及using System.IO Namespace),


        @"<Root>    
		   <Font Family=""Verdana"" Size=""7"" Color=""DarkRed"" /> 
		   <Line Color=""Red"" /> 
		</Root>";
StringBuilder output = new StringBuilder();
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
	XmlWriterSettings ws = new XmlWriterSettings();
    ws.Indent = true;
    using (XmlWriter writer = XmlWriter.Create(output, ws))
    {
		while (reader.Read())
		{
			switch (reader.NodeType)
			{
				case XmlNodeType.Element: // The node is an element.
					writer.WriteStartElement(reader.Name);
					while (reader.MoveToNextAttribute()) // Read the attributes.
					{
						writer.WriteStartAttribute(reader.Name);
						writer.WriteValue(reader.Value);
						writer.WriteEndAttribute();
					}
					break;
				case XmlNodeType.Text: //Display the text in each element.
					writer.WriteString(reader.Value);

					break;
				case XmlNodeType.EndElement: //Display the end of the element.
					writer.WriteEndElement();
					break;
			}
		}
	}
}
Console.Write(output.ToString());

 

但是輸出的結果,Font居然包含Line這個節點,如下,


  <Font Family="Verdana" Size="7" Color="DarkRed">
    <Line Color="Red" />
  </Font>
</Root>

 

看程式,一個開始,一個結束很正常呀~

但是如果一步一步Trace後發現,如果是空的節點,跟本不會跑到case XmlNodeType.EndElement裡面去。

如果是上面的XML內容,會跑進case XmlNodeType.EndElement裡的只會有Root節點而已。所以就會產生錯誤的XML內容出來。

再看一下XmlTextReader的屬性列表中,筆者發現一個IsEmptyElement的屬性(取得值,指出目前節點是否為空項目 (例如, <MyElement/>))。

所以,應該要在case XmlNodeType.Element的處理中加入判斷是否為空節點(bool isEmptyElem = reader.IsEmptyElement;),如果是的話,就要在最後加入writer.WriteEndElement。

最後修改後的程式如下,


				   @"<Root>    
					   <Font Family=""Verdana"" Size=""7"" Color=""DarkRed"" /> 
					   <Line Color=""Red"" /> 
					</Root>";
StringBuilder output = new StringBuilder();
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
	XmlWriterSettings ws = new XmlWriterSettings();
	ws.Indent = true;
	using (XmlWriter writer = XmlWriter.Create(output, ws))
	{
		while (reader.Read())
		{
			switch (reader.NodeType)
			{
				case XmlNodeType.Element: // The node is an element.
					bool isEmptyElem = reader.IsEmptyElement;
					writer.WriteStartElement(reader.Name);
					while (reader.MoveToNextAttribute()) // Read the attributes.
					{
						writer.WriteStartAttribute(reader.Name);
						writer.WriteValue(reader.Value);
						writer.WriteEndAttribute();
					}
					//因為是empty,結束時,要加上EndElement
					if (isEmptyElem)
						writer.WriteEndElement();
					break;
				case XmlNodeType.Text: //Display the text in each element.
					writer.WriteString(reader.Value);

					break;
				case XmlNodeType.EndElement: //Display the end of the element.
					writer.WriteEndElement();
					break;
			}
		}
	}
}
Console.Write(output.ToString());

 

這樣輸出就正常了,如下,


  <Font Family="Verdana" Size="7" Color="DarkRed" />
  <Line Color="Red" />
</Root>

所以使用XmlReader時,別忘了要考慮空節點哦!

 

參考資料

How to: Parse XML with XmlReader

XmlTextReader.IsEmptyElement

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^