前言:xml的操作方式有多種,但要論使用頻繁程度,博主用得最多的還是Linq to xml的方式,覺得它使用起來很方便,就用那麼幾個方法就能完成簡單xml的讀寫。之前做的一個專案有一個很變態的需求:C#專案呼叫不知道是什麼語言寫的一個WebService,然後新增服務引用總是失敗,通過代理的方式動態呼叫也總是報錯,最後沒辦法,通過傳送原始的WebRequest請求直接得到對方返回的一個xml檔案。注意過webservice的wsdl檔案的朋友應該知道這個是系統生成的xml檔案,有點複雜,研究了半天終於能正常讀寫了。今天在這裡和大家分享下。
1、介紹之前,首先回顧下Linq to xml的方式讀寫簡單xml的方法
(1)讀取xml
1 2 3 4 5 |
<?xml version="1.0" encoding="utf-8"?> <BizADsList> <adData aid="1" image="baidu.jpg" link="www.baidu.com" title="百度"/> <adData aid="2" image="qq.jpg" link="www.qq.com" title="騰訊"/> </BizADsList> |
1 2 3 4 5 6 7 8 9 10 11 12 |
var strPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\test.xml"); XDocument adList = XDocument.Load(strPath); var ad = from a in adList.Descendants("BizADsList").Elements("adData") select new { image = a.Attribute("image").Value, link = a.Attribute("link").Value, title = a.Attribute("title").Value }; string s = ""; foreach (var a in ad) s += a.image; |
(2)寫xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
/// <summary> /// 根據得到的Document集合生成XML /// </summary> /// <param name="lstDocumentBD"></param> /// <param name="docNE"></param> /// <param name="strSpiderTime"></param> /// <param name="strNewRate"></param> private static void SaveXmlByLstDocument(List<Document> lstDocumentBD, Document docNE, string strSpiderTime, string strNewRate) { try { XDocument xDoc = new XDocument(); XElement xRoot = new XElement(CSpiderConst.XML_ELE_ROOT); //1.構造Device節點 XElement xDevice = new XElement(CSpiderConst.XML_ELE_DEVICE); //2.構造NE節點 XElement xNE = new XElement(CSpiderConst.XML_ELE_NE); foreach (var oDocNE in docNE) { XElement xItem = new XElement(CSpiderConst.XML_ELE_ITEM, new XAttribute(CSpiderConst.XML_PROP_NAME, oDocNE.Key), oDocNE.Value); xNE.Add(xItem); } //這裡增加一個<Item name='NewRate'>和<Item name='SpiderTimeEx'>節點用來儲存當前這次的利用率和當次的採集時間 AddNewRateAndSpiderTime(strSpiderTime, strNewRate, xNE); xDevice.Add(xNE); //3.迴圈構造BD節點並新增到Device節點中 foreach (var oDocument in lstDocumentBD) { XElement xBD = new XElement(CSpiderConst.XML_ELE_BD); foreach (var oDocBD in oDocument) { XElement xItem = new XElement(CSpiderConst.XML_ELE_ITEM, new XAttribute(CSpiderConst.XML_PROP_NAME, oDocBD.Key), oDocBD.Value); xBD.Add(xItem); } AddNewRateAndSpiderTime(strSpiderTime, strNewRate, xBD); xDevice.Add(xBD); } xRoot.Add(xDevice); xDoc.Add(xRoot); //4.儲存到採集器本地,以伺服器的時間和網元的AssetID來命名 var strDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ReportFailed\\"); if (!Directory.Exists(strDirectoryPath)) { Directory.CreateDirectory(strDirectoryPath); } xDoc.Save(strDirectoryPath + docNE[TBLDeviceLCBB.PROP_ASSETID] + "_" + strSpiderTime.Replace(":", "_") + ".xml"); } catch { CLogService.Instance.Debug("儲存XML失敗"); } } |
通過XDocument、XElement物件和Element()、Elements()兩個方法能完成大部分xml檔案的操作。
2、進入今天的正題:讀寫帶名稱空間的xml檔案。
首先來看一段xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?xml version="1.0" encoding="utf-8" ?> <DataSet xmlns="http://WebXml.com.cn/"> <xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="getRegion"> <msdata:aa> test </msdata:aa> <xs:element name="getRegion" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> <xs:element name="Province"> <xs:sequence> <xs:element name="RegionID" type="xs:string" minOccurs="0"/> <xs:element name="RegionName" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:element> </xs:element> </xs:schema> </DataSet> |
第一次看到這個檔案確實讓人萌神了,比如需要取一個msdata:IsDataSet=”true”這個屬性,該怎麼取…
解析之前,先來分析下這段xml,<DataSet xmlns=”http://WebXml.com.cn/”>這段裡面有一個xmlns屬性,這個屬性是每一個標籤自帶的屬性,不信你可以新建一個xml檔案,然後在任何一個標籤裡面輸入xmlns屬性,後面都會出來很多的系統自帶的名稱空間。這個屬性表示所屬標籤在哪個名稱空間下面,所以在取的時候要帶上這個名稱空間。
先來看看解析的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 |
var strPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\test.xml"); var oRoot = XDocument.Load(strPath); //取DataSet標籤 var oDataSet = oRoot.Element(XName.Get("DataSet", "http://WebXml.com.cn/")); //取schema標籤 var oSchema = oDataSet.Element(XName.Get("schema", "http://www.w3.org/2001/XMLSchema")); //取element標籤 var oElement = oSchema.Element(XName.Get("element", "http://www.w3.org/2001/XMLSchema"));//這兩個節點都是以xs打頭,所以名稱空間都是xs的名稱空間 //取element標籤下面的IsDataSet屬性 var oElementValue = oElement.Attribute(XName.Get("IsDataSet", "urn:schemas-microsoft-com:xml-msdata")); //取aa標籤 var oAA = oSchema.Element(XName.Get("aa", "urn:schemas-microsoft-com:xml-msdata")); |
我們來解析下幾個關鍵的地方:
(1)我們來解析下
1 |
<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="getRegion"> |
這一句,最前面的”xs”表示標籤所屬名稱空間的變數,xmlns:xs=”http://www.w3.org/2001/XMLSchema”這個表示xs這個名稱空間的值。所以要得到schema這個標籤需要帶上名稱空間var oSchema = oDataSet.Element(XName.Get(“schema”, “http://www.w3.org/2001/XMLSchema”));這個標籤還定義了另一個名稱空間xmlns:msdata=”urn:schemas-microsoft-com:xml-msdata”。
(2)再來看看aa標籤
1 2 3 |
<msdata:aa> test </msdata:aa> |
msdata就是上面schema標籤裡面定義的另一個名稱空間,表示aa標籤屬於msdata名稱空間下面。
(3)再看來看屬性的取法:
1 |
<xs:element name="getRegion" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> |
如果要取msdata:IsDataSet=”true”,由於這個屬性也帶有名稱空間,所以取屬性時也要加上名稱空間了。所以需要這樣取。
var oElementValue = oElement.Attribute(XName.Get(“IsDataSet”, “urn:schemas-microsoft-com:xml-msdata”));
現在大夥們應該對這種xml有一個更加清晰的認識了吧。其實一般情況下這種場景比較少見,因為這麼複雜的xml一般是由引用服務時代理物件去解析的。但如果真的有這麼變態的需求我們也不用擔心了。在此記錄下,以後如果大家遇到希望能節約大夥的時間。