昨天用wpf的時候要訪問xml,弄了很久,所以記錄一下。
C#3.0提供了輕型的XmlReader、XmlWriter類和完全文檔對象模式(DOM)的XmlDocument類。這也是我昨天鬱悶半天的原因。以前用過XmlTextReader,也沒搞太懂,就是找到了方法,貼上代碼。看懂下了事。
在這個部落格可以找到原理:http://www.cnblogs.com/xdesigner/archive/2008/05/15/1198398.html
整個部落格可以看到xml操作方法:http://www.cnblogs.com/surfsky/archive/2007/03/13/673625.html
先上原理:
XmlReader是串流模型,所謂串流是指將XML文檔當做一個資料流來進行處理,逐個處理XML文檔中的資料。這種模型下,讀取快,記憶體佔用少,效能也就好。它的缺點也明顯(昨天我就是在這上面悶了很久):1.只能讀取不能修改;2.不能使用XPath技術;3.介面簡單,處理方法比較少。
System.Xml空間
以下類適合快速流式讀寫XML檔案(註:DOM適合隨機讀寫)
XmlReader, XmlWriter,
XmlTextReader, XmlTextWriter
XmlValidatingReader, XmlValidatingWriter 添加了DTD和模式驗證,因此提供了資料的有效性驗證
XmlNodeReader, XmlNodeWriter 把XmlNode作為其源
XmlDocument是DOM處理模型,使用文件物件模型的思想解析整個XML文檔,在記憶體中產生一個對象樹來表述XML文檔。可以使用XmlElement對象來影射XML文檔中的一個元素,使用XmlAttribute對象來影射到XML文檔中的一個屬性。這樣通過編程可以方便的操作XML,比如遞迴遍曆XML文檔的一部分或全部,可以像樹狀結構插入,修改或刪除XML元素,可以設定XML元素的屬性。
在DOM模式下,我們可以使用XPath技術在XML文檔樹狀結構中進行快速檢索和定位,這為處理XML文檔帶來比較大的方便。
經常用到的幾個DOM的類型;
XmlNode 是DOM結構中的所有類型的基礎類型,它定義了所有XML節點的通用屬性和方法,是XMLDOM的基礎。它具有一個ChildNodes屬性,表示它所包含的子XML節點。
XmlAttribute 表示XML屬性,它只儲存在XmlElement的Attributes 列表中。
XmlDocument表示XML文檔本身,是XMLDOM模型中的頂級對象,它用於對XML文檔進行整體的控制,並且是其它程式訪問XML文檔對象樹的唯一入口。
XmlLinkedNode在XmlNode的基礎上實現了訪問前後同級節點的方法。
XmlElement元素表示XML元素。是XMLDOM中使用最多的物件類型。它具有Attributes屬性可以處理它所擁有的屬性,可以使用ChildNodes屬性獲得它所有的子節點。並提供了一些添加和刪除子節點的方法。
下面看一張XDocument的(有個問題想問,XDocument和XElement的關係是?):
再上建議:
使用XmlDocument:1.傳統的DOM方法適用。2.所需的整個文檔皆在記憶體中。3.必須在記憶體中向前或向後遍曆以及更新。
使用XmlReader/XmlWriter:1.必須適用流方法。2.必須要求進行最快的處理。3.要處理較大的文檔並且無法一次性載入全部文檔。
使用XElement/XDocument:1.用代碼構建xml聲明。2.LINQ的查詢能力有助於流方法。3.需要一種針對大多數開發人員的更易讀的XML編程文法。
4.要求使用查語義在記憶體中遍曆。
博文後面的代碼比較多,我只說我用到的。
XmlReader有個很經典的:
XmlReader reader = xDoc.CreateRedaer();reader.Settings.CheckCharacters = true;int level = 0;while(reader.Reader()){ switch(reader.NodeType) { case XmlNodeType: Display(level,"CDATA:{0}",reader.Value); break; case XmlNodeType.Comment: Display(level,"Comment:{0}",reader.Value); break; case XmlNodeType.DocumentType: Display(level,"DOCTYPE:{0}= {1}",reader.Name,reader.Value); break; case XmlNodeType.Element: Display(level,"Element:{0}",reader.Name); level++; while(reader.MoveToNextAttribute()) { Display(level,"ATTRIBUTE: {0}='{1}'",reader.Name,reader.Value); } break; case XmlNodeType.EndElement; level--; break; case XmlNodeType.EntityReference: Display(level,"ENTITY:{0}",reader.Name); break; case XmlNodeType.ProcessingInstruction: Display(level,"INSTRUCTION: {0}={1}",reader.Value); break; case XmlNodeType.Text; Display(level,"TEXT:{0}",reader.Value); break; case XmlNodeType.XmlDeclaration: Display(level,"DECLARATION: {0}={1}",reader.Value); break; }}static void Display(int indentLevel,string format,params object[] args){ for(int i=0;i<indentLevel;i++) Console.Write(" "); Console.WriteLine(format,args)}
XDocument的(網上有更詳細的,這個是我為瞭解決自己的問題寫的,主要是遍曆某個節點下的全部子節點):
XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(filePath); XmlNodeList topM = xmldoc.DocumentElement.ChildNodes; foreach (XmlElement element in topM) { if(element.Name=="Vocabulary") { XmlNodeList nodelist = element.ChildNodes; Model.Vocabulary vocabulary = new Model.Vocabulary(); foreach (XmlElement el in nodelist) { if (el.Name == "Word") vocabulary.Word = el.InnerText; else if (el.Name == "Explain") vocabulary.Explain = el.InnerText; else if (el.Name == "IsLastSelected") vocabulary.IsLastSelected = el.InnerText=="0"?false:true; } vocabularies.Add(vocabulary); }OK,先到這裡,以後碰到問題再補上增刪的。
繼續:
關於linq to xml的,關於Descentdants函數。具體可以參看:http://www.programbbs.com/doc/4564.htm
Desentdants英文意思是後代,晚輩。也就是這個函數是用於獲得某個Element下的指定子Element。
foreach (XElement elem in doc.Elements(\"Customers\").Descendants(\"Customer\")) { Console.WriteLine(string.Format( \"Customer ID : {0}, Name : {1}, Address : {2}\", elem.Attribute(\"ID\").Value, elem.Attribute(\"Name\").Value, elem.Attribute(\"Address\").Value));} <?xml version=\"1.0\" encoding=\"utf-8\"?> <Customers><Customer ID=\"A0001\" Name=\"Tom\" Address=\"Taipen\" /> <Customer ID=\"A0002\" Name=\"Mary\" Address=\"LD\" /> <Customer ID=\"A0003\" Name=\"Jeff\" Address=\"YW\" /> </Customers>
特別的,當xml中包含了Namespace時,傳入的Descendants的指定Element名稱必包含Namespace,
Descendants函數接收的是一個XName的元素,它隱含了\運算元,顯然剛只得到了Element的LocalName。可以這樣做:
var cstr = (from s1 in doc.Descendants() where s1.Name.LocalName==\"Connection\" select s1).First();
var cstr = (from s1 in doc.Descendants(doc.Root.Name.Namespace+\"Connection\" select s1).First();
繼續:Linq to xml 的Elements()函數
(發現很多方法都可以輕鬆搞定我那個問題)
參考網站:http://www.cnblogs.com/ucetgg/archive/2009/04/23/1441739.html
var name = from nm in xelement.Elements("Employee") where (string)nm.Element("Sex")=="Female" select nm;
繼續:處理非法字元
XElement和XmlWriter方法:
XElement可以直接處理轉義也可以用XCData對象處理。
string invalidChars =@"<>\&";XElement element = new XElement("Root", new XElement("InvalidChars1", new XCData(invalidChars)), new XElement("InvalidChars2", invalidChars));InvalidChars1將儲存為<![CDATA[<>\&"]>,InvalidChars2將轉義儲存。
附上轉義表:
< <
< >
" "
' '
& &
XmlWriter處理可以使用WriteCData方法將非法字元封裝在CDATA片段中,另一種使用WriteElementString方法。
string invalidChars =@"<>\&";XmlWriterSettings settings = new XmlWriterSettings();settings.Indent = true;using(XmlWriter writer = XmlWriter.Create(Console.Out,settings)){writer.WriteStartElement("root");writer.WriteStartElement("InvalidChars1");writer.WriteCDate(invalidChars);writer.WriteEndElement();writer.WriteElementString("InvalidChars2",invalidChars);writer.WriteEndElement();}XmlDocumenthe和XmlElement處理方法:
放置於CDATA片段中,並添加至XmlElement的InnerXML屬性中。
string invalidChars =@"<>\&";XmlElement invalidElement1 = xmlDoc.CreateElement("InvalidChars1");invalidElement1.AppendChild(xmlDoc.CreateCDataSection(invalidChars);通過賦值給InnerText屬性令XmlElement類為封裝資料。
string invalidChars =@"<>\&";XmlElement invalidElement2 = xmlDoc.CreateElement("InvalidChars2");invalidElement2.InnerText = invalidChars;