• PowerShell笔记


    本系列是一个重新学习PowerShell的笔记,内容引用自PowerShell中文博客

    XML是”可扩展标记语言“的缩写,是一种对于任意结构化的信息的可描述性语言。过去处理XML还是相当麻烦的,但是现在PowerShell中,对XML有了非常优秀的支持。通过它的帮助,你既可以非常容易的在XML中包装数据,也可以非常舒服的访问已有的XML文件。

    XML 结构

    XML使用标签来唯一标识信息片段,一个标签像网站中的HTML文档使用的一样,是一对尖括号。通常,一条信息被一个起始和结束标记所分割。结束标记前面使用“/”,其结果谓之结点,在下面的例子就应当叫做Name结点。

    <Name>Tobias Weltner</Name>
    

    另外,结点拥有它自身相关信息的属性(attributes)。这些信息位于开始标签。

    <staff branch="Hanover" Type="sales">...</staff>
    

    如果一个结点为空,它的开始结点和结束结点可以折叠起来。结束符号“/”指向标签结束。例如,在Hanover的子公司没有任何员工从事销售工作,那这个标签可以像这样描述。

    <staff branch="Hanover" Type="sales"/>
    

    通常,如果一个标签不空,包含更多信息,这些信息应当包含在标签中。这就允许按照你喜欢的深度产生信息结构。下面XML结构描述Hanover子公司销售部门工作的两位员工。

    <staff branch="Hanover" Type="sales">
        <employee>
            <Name>Tobias Weltner</Name>
            <function>management</function>
            <age>39</age>
        </employee>
        <employee>
            <Name>Cofi Heidecke</Name>
            <function>security</function>
            <age>4</age>
        </employee>
    </staff>
    

    为了让XML文件被识别,通常在它们的开始有一个非常简单类似下面例子中的一个头声明。

    <?xml version="1.0" ?>
    

    头声明声明了紧跟其后的XML符合1.0 版本规范。被称为”schema”的东西也可以在这里被指定。具体来说,Schema有个XSD文件的形式,它用来描述XML文件结构应当遵循确定的规范。在上一个例子中,Schema可能会指定必须包含“staff”结点作为员工的信息,进而指定多个命名为“staff”的子结点为必须的。Schema也可以指定相关的信息,例如每个员工的名称和定义他们具体的职能。

    因为XML文件有纯文本构成,你可以使用编辑器创建他们,也可以直接使用PowerShell。让我们将前面定义的员工信息保存为一个XML文件:

    
    PS C:PowerShell> $xml = @'
    >> <?xml version="1.0" standalone="yes"?>
    >> <staff branch="Hanover" Type="sales">
    >> <employee>
    >> <Name>Tobias Weltner</Name>
    >> <function>management</function>
    >> <age>39</age>
    >> </employee>
    >> <employee>
    >> <Name>Cofi Heidecke</Name>
    >> <function>security</function>
    >> <age>4</age>
    >> </employee>
    >> </staff>
    >> '@ | Out-File employee.xml                                                                                           PS C:PowerShell> ls
    
    
        Directory: C:PowerShell
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    -a----        2021/9/22     11:16            556 employee.xml
    

    注意:XML文件大小写敏感的!

    加载和处理XML文件

    访问Xml

    如果你想将XML文件按照实际的XML来处理,而不是纯文本。文件的内容必须转换成XML类型。类型转换在第六章已经提到,只须一行。

    PS C:PowerShell> $xmldata = [xml](Get-Content employee.xml)                                                            PS C:PowerShell> $xmldata                                                                                              
    xml                            staff
    ---                            -----
    version="1.0" standalone="yes" staff
    

    然而,转换只会在指定的xml文本合法并不包含解析错误时有效。当你尝试转换的xml文件结构不合法,会报错。
    用来描述XML的结构和信息现在被存放在变量$xmldata。从现在开始,获取一小段信息将会变得非常容易,因为XML对象将每个结点转换成了属性。你可以像这样获取员工信息:

    PS C:PowerShell> $xmldata.staff.employee                                                                               
    Name           function   age
    ----           --------   ---
    Tobias Weltner management 39
    Cofi Heidecke  security   4
    

    访问和更新单个结点

    如果一个结点在xml中是唯一的,你可以像前面的例子一样输入一个句号来访问它。然而,多数情况下XML文档包含了许多类似的结点(被称为兄弟结点),像前面最后一个例子中一样,包含了多个独立的员工。比如,你可以使用管道获得特定的员工,然后来更新它的数据。

    PS C:PowerShell> $xmldata.staff.employee |
    >> Where-Object { $_.Name -match "Tobias Weltner" }                                                                     
    Name           function   age
    ----           --------   ---
    Tobias Weltner management 39
    
    PS C:PowerShell> $employee = $xmldata.staff.employee |
    >> Where-Object { $_.Name -match "Tobias Weltner" }                                                                     PS C:PowerShell> $employee.function = "vacation"                                                                       PS C:PowerShell> $xmldata.staff.employee | ft -autosize                                                                
    Name           function age
    ----           -------- ---
    Tobias Weltner vacation 39
    Cofi Heidecke  security 4
    

    使用SelectNodes()来选择Nodes

    SelectNodes()方法是Xpath查询语言支持的方法,也允许你选择结点。XPath指的是一个结点‘路径名称’:

    PS C:PowerShell> $xmldata = [xml](Get-Content employee.xml)                                                            PS C:PowerShell> $xmldata.SelectNodes("staff/employee")                                                                
    Name           function   age
    ----           --------   ---
    Tobias Weltner management 39
    Cofi Heidecke  security   4
    

    结果看起来像前面直接通过属性访问一样,但是XPath支持在方括号中使用通配符访问。
    下面的语句只会返回第一个员工结点。

    PS C:PowerShell> $xmldata.SelectNodes("staff/employee[1]")                                                             
    Name           function   age
    ----           --------   ---
    Tobias Weltner management 39
    

    如果你想,你还可以获取一个年龄小于18岁的员工列表:

    
    PS C:PowerShell> $xmldata.SelectNodes("staff/employee[age<18]")                                                        
    Name          function age
    ----          -------- ---
    Cofi Heidecke security 4
    

    类似的方式,查询语言也支持获取列表中的最后一位员工信息,所以也可以指定位置:

    PS C:PowerShell> $xmldata.SelectNodes("staff/employee[last()]")                                                        
    Name          function age
    ----          -------- ---
    Cofi Heidecke security 4
    
    
    PS C:PowerShell> $xmldata.SelectNodes("staff/employee[position()>1]")                                                  
    Name          function age
    ----          -------- ---
    Cofi Heidecke security 4
    

    或者,你可以使用所谓的XpathNavigator,从中获取许多从XML文本的类型转换。

    # 创建一个 XML定位:
    PS C:PowerShell> $xpath = [System.XML.XPath.XPathDocument]`
    >> [System.IO.TextReader][System.IO.StringReader]`
    >> (Get-Content employee.xml | out-string)                                                                              PS C:PowerShell> $navigator = $xpath.CreateNavigator()
    
    # 输出Hanover子公司的最后一位员工
    PS C:PowerShell> $query = "/staff[@branch='Hanover']/employee[last()]/Name"                                            PS C:PowerShell> $navigator.Select($query) | Format-Table Value                                                        
    Value
    -----
    Cofi Heidecke
    
    # 输出Hanover子公司的除了Tobias Weltner之外的所有员工,
    PS C:PowerShell> $query = "/staff[@branch='Hanover']/employee[Name!='Tobias Weltner']"                                                                                                           PS C:PowerShell> $navigator.Select($query) | Format-Table Value                                                        
    Value
    -----
    Tobias Weltnermanagement39
    Cofi Heideckesecurity4
    

    基于Namespace来SelectNode

    上面使用PowerShell处理xml文档时,用到了一个selectNode。但是有时候按照上面代码执行就是看不到期望的结果。原因可能是因为你的XML文档中多了个命名空间(Namespace)。没有命名空间时,Name就是Name,有了命名空间后,Name=Namespace+Name。

    看下面的例子,有一个XML文档,要求输出“Obj”的结点信息。

    PS C:PowerShell> @"
    >> <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
    >>   <Obj RefId="0">
    >>     <TN RefId="0">
    >>       <T>Selected.System.IO.FileInfo</T>
    >>       <T>System.Management.Automation.PSCustomObject</T>
    >>       <T>System.Object</T>
    >>     </TN>
    >>     <MS>
    >>       <S N="Name">test.txt</S>
    >>       <I64 N="Length">143</I64>
    >>       <DT N="CreationTime">2013-12-01T20:05:22.5568032+08:00</DT>
    >>     </MS>
    >>   </Obj>
    >> </Objs>
    >> "@ | Out-File namespacexml.xml                                                                                       PS C:PowerShell> ls                                                                                                    
    
        Directory: C:PowerShell
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    -a----        2021/9/22     11:50            412 employee.xml
    -a----        2021/9/16     13:55            812 Error.txt
    -a----        2021/9/22     11:59            840 namespacexml.xml
    

    如果直接使用SelectNodes方法就会打印下面的信息

    PS C:PowerShell> $xmldata = [xml](Get-Content namespacexml.xml)
    PS C:PowerShell> $xml.DocumentElement.SelectNodes("/Objs/Obj")
    At line:1 char:45
    + $xml.DocumentElement.SelectNodes("/Objs/Obj")
    +                                             ~
    Missing ')' in method call.
    At line:1 char:45
    + $xml.DocumentElement.SelectNodes("/Objs/Obj")
    +                                             ~
    Unexpected token ')' in expression or statement.
        + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : MissingEndParenthesisInMethodCall
    

    此时就需要加入名命空间引用

    PS C:PowerShell> $ns= new-object System.Xml.XmlNamespaceManager $xmldata.NameTable                                     PS C:PowerShell> $ns.AddNamespace("myNS", $xmldata.DocumentElement.NamespaceURI)                                       PS C:PowerShell> $xmldata.DocumentElement.SelectNodes("//myNS:Objs//myNS:Obj",$ns)                                     
    RefId TN MS
    ----- -- --
    0     TN MS
    

    但是我一般不用XML中的SelectNodes,PowerShell中有更方便的方法,请看:

    PS C:PowerShell> $xmldata.Objs.Obj                                                                                     
    RefId TN MS
    ----- -- --
    0     TN MS
    

    访问属性

    属性是定义在一个XML标签中的信息,如果你想查看结点的属性,可以使用get_Attributes()方法:

    PS C:PowerShell> $xmldata.staff.get_Attributes()                                                                       
    #text
    -----
    Hanover
    sales
    

    使用GetAttribute()方法来查询一个特定的属性:

    PS C:PowerShell> $xmldata.staff.GetAttribute("branch")                                                                 Hanover
    

    使用SetAttribute()方法来指定新的属性,或者更新(重写)已有的属性。

    PS C:PowerShell> $xmldata.staff.SetAttribute("branch", "New York")                                                     PS C:PowerShell> $xmldata.staff.GetAttribute("branch")                                                                 New York
    

    添加新结点

    如果你想在员工结点列表中添加新的员工。首先,使用CreateElement()创建一个员工元素,然后定制员工的内部结构。最后,就可以在XML结构中你期望的位置插入这个元素。

    PS C:PowerShell> # 加载XML文本文件:                                                                                   
    PS C:PowerShell> $xmldata = [xml](Get-Content employee.xml)                                                            PS C:PowerShell> # 创建新的结点:                                                                                      
    PS C:PowerShell> $newemployee = $xmldata.CreateElement("employee")                                                     PS C:PowerShell> $newemployee.set_InnerXML("<Name>Bernd Seiler</Name><function>expert</function>")                                                              PS C:PowerShell> # 插入新结点:                                                                                        
    PS C:PowerShell> $xmldata.staff.AppendChild($newemployee)                                                              
    Name         function
    ----         --------
    Bernd Seiler expert
    
    
    PS C:PowerShell> # 验证结果:                                                                                           PS C:PowerShell> $xmldata.staff.employee                                                                               
    Name           function   age
    ----           --------   ---
    Tobias Weltner management 39
    Cofi Heidecke  security   4
    Bernd Seiler   expert
    
    PS C:PowerShell> # 输出为纯文本:                                                                                      
    PS C:PowerShell> $xmldata.get_InnerXml()                                                                               <?xml version="1.0" standalone="yes"?>
    <staff branch="Hanover" Type="sales">
    <employee><Name>Tobias Weltner</Name><function>management</function><age>39</age></employee><employee><Name>Cofi Heidecke</Name><function>security</function><age>4</age></employee><employee><Name>Bernd Seiler</Name><function>expert</function></employee>
    </staff>
    

    保存XML文件

    PS C:PowerShell>  &{(Get-Location).Path + "employee.xml"}                                                             C:PowerShellemployee.xml
    PS C:PowerShell> $xmldata.Save( ((Get-Location).Path + "employee.xml"))                                               PS C:PowerShell> ls                                                                                                    
    
        Directory: C:PowerShell
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    -a----        2021/9/22     11:50            412 employee.xml
    
  • 相关阅读:
    Spring:dispatchservlet
    信息系统设计
    数据流图的绘制方法
    信息系统管理工程师学习笔记
    JS语法学习笔记
    jQuery
    用Excel生成Sql
    JAVA-Reflect
    Java创建对象的过程
    有关死锁那点事儿
  • 原文地址:https://www.cnblogs.com/MerLin-LiuNian/p/15319511.html
Copyright © 2020-2023  润新知