在大數據項目開發過程中,ETL(Extract-Transform-Load)是必不可少。即便目前 JSON 非常流行,開發人員也有必定會有對遠古系統的挑戰,而 XML 格式的數據源作為經典存在渾身上下散發着濃濃 old money 的味道。
因為有 Newtonsoft.Json 這樣優秀的 JSON 框架存在,開發人員可以很容易的對 JSON 格式的字符串反序列化。但是 XML 格式的數據就沒有這么方便了:雖然 .NET 中內置了對 XML 序列化和反序列化的支持,但遇到需要對接外部數據時就不是很方便了。
使用 XmlReader 讀取數據
從 XML 中提取目標數據最高效,也最麻煩的方式是直接使用 XmlReader :
<employee xmlns="urn:empl-hire"> <ID>12365</ID> <hire-date>2003-01-08</hire-date> <title>Accountant</title> </employee>
使用以下代碼對上述 hireDate.xml 文件讀取:
using (XmlReader reader = XmlReader.Create("hireDate.xml")) { // Move to the hire-date element. reader.MoveToContent(); reader.ReadToDescendant("hire-date"); // Return the hire-date as a DateTime object. DateTime hireDate = reader.ReadElementContentAsDateTime(); Console.WriteLine("Six Month Review Date: {0}", hireDate.AddMonths(6)); }
輸出:
Six Month Review Date: 7/8/2003 12:00:00 AM
使用 XDocument 讀取數據
在 .NET Framework 3.5 發布后的時間里,開發人員可以使用 XDocument 來生成和解析 XML 文檔,這要比 XmlReader 方便的多:
string str = @"<?xml version=""1.0""?> <!-- comment at the root level --> <Root> <Child>Content</Child> </Root>"; XDocument doc = XDocument.Parse(str); Console.WriteLine(doc.XPathSelectElement("//Child"));
輸出:
<Child>Content</Child>
但硬編碼的 XPath 並不方便調試,而且需要時刻關注空引用的問題。在 XML 格式復雜、項目工程比較大時使用起來也不方便。
一種把 XML 轉換為 XML 的技術: XSLT
在計算機科學中,可擴展樣式表轉換語言(英語:Extensible Stylesheet Language Transformations,縮寫XSLT)是一種樣式轉換標記語言,可以將XML數據檔轉換為另外的XML或其它格式,如HTML網頁,純文字。XSLT最末的T字母表示英語中的“轉換”(transformation)。
簡單來說,開發人員可以借助 XSLT 技術編寫一個 XML 文件,並使用該文件將一種 XML 格式轉換為另一種 XML 。即:在對接復雜格式 XML 數據源時,開發人員可以編寫一個后綴為 .xsl 的文件,並使用該文件將數據源格式轉換為自己需要的格式(比如可以適配 XML 反序列化的格式)。
從一個簡單的 XML 文件開始:
<?xml version="1.0" encoding="ISO-8859-1"?> <catalog> <cd> <title>Empire Burlesque</title> <artist>Bob Dylan</artist> <country>USA</country> <company>Columbia</company> <price>10.90</price> <year>1985</year> </cd> . . . </catalog>
如果直接在瀏覽器打開這個文件:
假設我們只關心所有的 title 信息,可以使用下面的 cdcatalog.xsl 文件,該文件可以將 cdcatalog.xml 轉為 XmlSerializer 所需要的格式:
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsl:for-each select="catalog/cd"> <string> <xsl:value-of select="title"/> </string> </xsl:for-each> </ArrayOfString> </xsl:template> </xsl:stylesheet>
為了可以在瀏覽器中直接觀察到轉換效果,可以選擇把 XSL 樣式表鏈接到 XML 文檔:向 XML 文檔(”cdcatalog.xml”)添加 XSL 樣式表引用即可。
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet type="text/xsl" href="cdcatalog.xsl"?> <catalog> <cd> <title>Empire Burlesque</title> <artist>Bob Dylan</artist> <country>USA</country> <company>Columbia</company> <price>10.90</price> <year>1985</year> </cd> . . . </catalog>
刷新瀏覽器,打開開發者工具:
也可以在: https://www.coderbusy.com/demos/2021/1531/cdcatalog.xml 查看在線示例。
從上面的操作可以看出,調試 XLS 文件的成本是很低的,開發者可以很容易對 XLS 文件進行更改,並在短時間之內得到運行結果。
在 C# 中使用 XSLT 技術
在 C# 中,可以使用 XslCompiledTransform 進行 XSL 轉換。以下代碼展示這個轉換過程:
XslCompiledTransform xsl = new XslCompiledTransform(); xsl.Load("cdcatalog.xsl"); var sb = new StringBuilder(); using (var sw = new StringWriter(sb)) { using (var xw = new XmlTextWriter(sw) { Formatting = Formatting.Indented }) { xsl.Transform("cdcatalog.xml", xw); } } var xml = sb.ToString(); Console.WriteLine(xml);
以上代碼會產生如下輸出:
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <string>Empire Burlesque</string> <string>Hide your heart</string> <string>Greatest Hits</string> <string>Still got the blues</string> <string>Eros</string> . . . </ArrayOfString>
反序列化 XML 字符串
轉換 XML 不是目的,能直接拿到數據對象才是。以上的代碼完成了格式轉換,接着需要對轉換好的 XML 字符串反序列化:
var xmlSerializer = new XmlSerializer(typeof(List<string>)); using (var ms = new emoryStream(Encoding.UTF8.GetBytes(xml))) { var list = (List<string>) xmlSerializer.Deserialize(ms); foreach (var item in list) { Console.WriteLine(item); } }
以上代碼借助 XmlSerializer 實現了反序列化功能,這會產生以下輸出:
Empire Burlesque Hide your heart Greatest Hits Still got the blues Eros ...
總結與源碼
本文所述的轉換和反序列化技術已經在真實的生產環境中得到驗證,千萬級的數據處理也毫不費力。
本文包含的演示的代碼和數據可以在 Gitee 上找到: https://gitee.com/coderbusy/demo/tree/master/hello-xslt/HelloXslt 。