• LINQ to XML


    前言


    可扩展标记语言(Extensible Markup Language,XML)是一种标记语言,它定义了一组规则,用于以人和机器都可以理解的格式对文档进行编码。下面是一个简单的XML示例:

    <note>
    <to>George</to>
    <from>John</from>
    <heading>Reminder</heading>
    <body>Don't forget the meeting!</body>
    </note>
    

    本文首先介绍Sysem.Xml命名空间中处理XML的一般方法,如XmlReader、XmlWriter以及XmlDocument等。随后讲解如何通过LINQ to XML操作XML,体会LINQ的方便与快捷。

    System.Xml


    本文将使用product.xml作为数据源,以下是文件结构:

    <?xml version="1.0" encoding="utf-8" ?>
    <products>
      <product vendor="Apple" productiondate="2018/01/01">
        <name>IphoneX</name>
        <price>7088</price>
      </product>
      <product vendor="Huawei" productiondate="2018/01/12">
        <name>Huawei P20 Pro</name>
        <price>6288</price>
      </product>
    </products>
    

    XmlReader

    表示提供对XML数据进行快速、非缓存、只进访问的读取器。

    首先来看一个非常简单的示例:

    XmlReader xr = XmlReader.Create("product.xml");
    while (xr.Read())
    {
        if (xr.NodeType == XmlNodeType.Text)
        {
            Console.WriteLine(xr.Value);
        }
    }
    Console.ReadLine();
    

    这段代码有几点需要注意:

    1. 使用静态方法XmlReader.Create创建XmlReader对象
    2. 遍历文档时,调用Read方法进入下一节点
    3. 通过Value属性获取节点的值

    当然,System.Xml命名空间中读取XML文档的方式很多(例如,可以使用EOF属性作为循环条件),这里就不一一介绍了。

    使用XmlReader类进行验证

    XmlReader类可以使用XmlReaderSettings类,根据XSD架构验证XML的有效性。下面的示例使用product.xsd验证product.xml文档。xsd文件结构:

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:element name="products">
        <xs:complexType>
          <xs:sequence>
            <xs:element maxOccurs="unbounded" name="product">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="name" type="xs:string" />
                  <xs:element name="price" type="xs:unsignedShort" />
                </xs:sequence>
                <xs:attribute name="vendor" type="xs:string" use="required" />
                <xs:attribute name="productiondate" type="xs:string" use="required" />
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
    

    向product.xml添加如下内容,注意:这里故意将product节点错写为products

    <products vendor="Xiaomi" productiondate="2018/01/12">
        <name>Xiaomi MIX 2</name>
        <price>2599</price>
    </products>
    

    客户端调用

    static void Main(string[] args)
    {
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.Schemas.Add(null, "product.xsd");
        settings.ValidationType = ValidationType.Schema;
        settings.ValidationEventHandler += new System.Xml.Schema.ValidationEventHandler(settings_ValidationEventHandler);
        XmlReader xr = XmlReader.Create("product.xml", settings);
        while (xr.Read())
        {
            if (xr.NodeType == XmlNodeType.Text)
            {
                Console.WriteLine(xr.Value);
            }
        }
        Console.ReadLine();
    }
    
    static void settings_ValidationEventHandler(object sender, System.Xml.Schema.ValidationEventArgs e)
    {
        Console.WriteLine(e.Message);
    }
    

    说明:

    • 创建XmlReaderSettings后,将product.xsd架构添加到XmlSchemaSet对象中。添加时第一个参数设置为null,默认使用当前的Namespace。
    • 当文档验证失败时,会引发一个XmlSchemaValidationException异常,这里使用ValidationEvent处理验证失败,避免引发异常。

    输出结果

    XmlWriter

    表示一个编写器,该编写器提供一种快速、非缓存和只进的方式来生成包含 XML 数据的流或文件。

    下面是一个简单的示例,说明如何使用XmlWriter类:

    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    settings.NewLineOnAttributes = true;
    XmlWriter xw = XmlWriter.Create("newProduct.xml", settings);
    xw.WriteStartDocument();
    xw.WriteStartElement("products");
    xw.WriteStartElement("product");
    xw.WriteAttributeString("vendor", "Xiaomi");
    xw.WriteAttributeString("productiondate", "2018/01/12");
    xw.WriteElementString("name", "Xiaomi MIX 2");
    xw.WriteElementString("price", "2599");
    xw.WriteEndElement();
    xw.WriteEndElement();
    xw.WriteEndDocument();
    xw.Flush();
    xw.Close();
    

    输出结果:

    <?xml version="1.0" encoding="utf-8"?>
    <products>
      <product
        vendor="Xiaomi"
        productiondate="2018/01/12">
        <name>Xiaomi MIX 2</name>
        <price>2599</price>
      </product>
    </products>
    

    XmlDocument

    XmlDocument类是用于在.NET中表示DOM的类,与XmlReader和XmlWriter不同,XmlDocument类具有读写功能,并可以随意访问DOM树。下面介绍一个使用XmlDocument的示例:

    XmlDocument xd = new XmlDocument();
    xd.Load("product.xml");
    XmlNodeList nodeList = xd.GetElementsByTagName("name");
    foreach (XmlNode node in nodeList)
    {
        Console.WriteLine(node.OuterXml);
    }
    Console.ReadLine();
    

    XmlDocument和XmlReader加载文本的区别:

    XmlReader只浏览一次文档,它是一种只进访问的读取器。也就是说,如果需要反复查看节点,使用XmlReader是不合适的,这种情况下最好使用XmlDocument。

    LINQ to XML


    在介绍如何使用LINQ to XML操作XML文档之前,首先介绍几个相关的对象。

    • XDocument:XDocument取代之前的XmlDocument对象,使得处理XML文档更容易,XDocument对象调用Load方法将xml文件内容加载到一个XDocument对象中。

        XDocument xd = XDocument.Load("product.xml");
      
    • XElement:使用XElement可以轻松创建包含单个元素的对象。

        XElement xe = new XElement("product",
            new XElement("name", "vivo"),
            new XElement("price", "2899"));
      
    • XNamespace:XNamespace对象表示XML命名空间。在前面的例子中,给根元素应用一个命名空间。

        XNamespace xn = "http://www.xxx.com/";
        XElement xe = new XElement(xn + "product",
            new XElement("name", "vivo"),
            new XElement("price", "2899"));
      
    • XComment:XComment对象可以将注释添加到XML文档中。

        XDocument xd = XDocument.Load("product.xml");
        XComment xc = new XComment("this is a commet");
        xd.Add(xc);
      
    • XAttribute:通过XAttribute对象可以添加和使用XML特性。

        XElement xe = new XElement("product",
           new XAttribute("vendor", "vivo"),
           new XAttribute("productiondate","2018/03/05"),
           new XElement("name", "vivo"),
           new XElement("price", "2899"));
      

    查询XML文档

    下面的示例使用LINQ查询product.xml文件,获取所有的产品及价格。

    XDocument doc = XDocument.Load("product.xml");
    var query = from q in doc.Descendants("product")
                select q.Value;
    foreach (var product in query)
    {
        Console.WriteLine(product);
    }
    

    这里调用了XDocument对象的Descendants属性获取所有的product节点的子元素,接着select语句获取这些元素的值,最后循环输出这些值。输出结果如下:

    使用Element方法,还可以获取XML文档中特定节点。例如只需要查询所有产品的名字,只需要将LINQ语句中select表达式后面跟上q.Element("name").Value即可。

    XDocument doc = XDocument.Load("product.xml");
    var query = from q in doc.Descendants("product")
                select q.Element("name").Value;
    foreach (var product in query)
    {
        Console.WriteLine(product);
    }
    //-----------output---------------
    //IphoneX
    //Huawei P20 Pro
    //Xiaomi MIX 2
    //--------------------------------
    

    通过Attribute方法查询指定特性的节点,以下示例查询vendor特性为Apple的产品名:

    XDocument doc = XDocument.Load("product.xml");
    var query = from q in doc.Descendants("product")
                where q.Attribute("vendor").Value.Equals("Apple")
                select q.Element("name").Value;
    foreach (var product in query)
    {
        Console.WriteLine(product);
    }
    //-----------output:IphoneX----------
    

    通过Element方法查询指定子节点,以下示例查询Xiaomi MIX 2的价格:

    XDocument doc = XDocument.Load("product.xml");
    var query = from q in doc.Descendants("product")
                where q.Element("name").Value.Equals("Xiaomi MIX 2")
                select q.Element("price").Value;
    foreach (var product in query)
    {
        Console.WriteLine(product);
    }
    //-----------output:2599-------------
    

    Elements和Descendants的区别:

    同样是获取指定节点,Descendants方法获取XDocument中所有符合条件的节点,而Elements方法仅仅能够获取当前节点的下一层中所有符合条件的节点。举个例子来说,如下xml文档结构:

    <Employees>
      <Employee>
        <ID>0001</ID>
        <Name>Zhang San</Name>
      </Employee>
      <Employee>
        <ID>0002</ID>
        <Name>Li Si</Name>
        <New>
          <Employee>
            <ID>0020</ID>
            <Name>Wang Wu</Name>
          </Employee>
        </New>
      </Employee>
    </Employees>
    

    使用Elements和Descendants方法查询所有Employee的姓名:

    XDocument doc = XDocument.Load("employee.xml");
    var query1 = from q in doc.Root.Elements("Employee")
                 select q.Element("Name").Value;
    var query2 = from q in doc.Descendants("Employee")
                 select q.Element("Name").Value;
    
    Console.WriteLine("Query1:");
    foreach (var result in query1)
    {
        Console.WriteLine(result);
    }
    Console.WriteLine("-----------------------------");
    Console.WriteLine("Query2:");
    foreach (var result in query2)
    {
        Console.WriteLine(result);
    }
    //-----------output---------------
    //Query1:
    //Zhang San
    //Li Si
    //-----------------------------
    //Query2:
    //Zhang San
    //Li Si
    //Wang Wu
    //--------------------------------
    

    根据输出可以看出,Query1只查询到根节点下一层中的员工信息,而Query2查询到文档中所有员工的信息。

    使用XSD文件进行格式验证

    任然将product节点错写为products,下面的示例使用product.xsd文件对xml进行格式验证。

    XDocument doc = XDocument.Load("product.xml");
    XmlSchemaSet settings = new XmlSchemaSet();
    settings.Add(null, XmlReader.Create("product.xsd"));
    doc.Validate(settings, (p, e) =>
    {
        Console.WriteLine(e.Message);
    });
    
    //output:The element 'products' has invalid child element 'products'. List of possible elements expected: 'product'.
    

    写入XML文档

    除了读取XML文档之外,同样可以轻松的写入文档。如下将product.xml中IPhoneX的价格修改为6888。

    XDocument doc = XDocument.Load("product.xml");
    doc.Root.Elements("product")
       .Where(p => p.Attribute("vendor").Value.Equals("Apple"))
       .Single()
       .Element("price")
       .SetValue("6888");
    doc.Save("product.xml");
    

    查看product.xml文档,发现价格已被修改:



    参考资料


    1. 《C#高级编程(第9版)》

    2. 微软官方文档 - LINQ to XML

  • 相关阅读:
    swift 中 Self 与self
    Swift 中的泛型
    mac 报文件已损坏 怎么办
    winxp秘钥
    字符串拷贝函数strcpy, strcat, sprintf, strncpy, strncat和snprintf的区别
    【原创】Linux应用程序完整调用自己写的字符设备驱动过程
    idea中新建git分支,并提交到远程github
    (JS-PHP)使用RSA算法进行加密通讯
    Linux日志相关的命令
    hibernate中关于is null的查询
  • 原文地址:https://www.cnblogs.com/Answer-Geng/p/9071541.html
Copyright © 2020-2023  润新知