如何更改 XmlElements 的顺序?

Wil*_* YK 6 xml powershell element xmlelement

我有一个包含各种元素的 XML,但其中一个元素名为RowId,我想移动到相应 XML 数组的顶部...本质上是按照我的方式对元素进行排序。我的下面的代码可以复制/粘贴供您测试。

  1. 实现这一目标的最佳方法是什么?和/或有比我现在正在做的更好的方法吗?这看起来很笨拙。

  2. 如果我包含以下代码,为什么会起作用| Sort-Object -Property "Name"

复制并粘贴以下代码:

$rawXML = [xml]@"
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Document">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Object1" minOccurs="0" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="Element1" nillable="true">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="80"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                            <xs:element name="Element2" nillable="true">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="-1"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                            <xs:element name="RowId">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="20"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
                <xs:element name="Object2" minOccurs="0" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="Element4" nillable="true">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="80"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                            <xs:element name="Element5" nillable="true">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="-1"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                            <xs:element name="RowId">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="20"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
"@

$rawXML.schema.element.complexType.sequence.element.complexType.sequence | ForEach-Object {
    $sequence = $_

    # This does NOT work
    $childNodes = $sequence.ChildNodes #| Sort-Object -Property "Name"

    $childNodes.Count # Output = 3
    $sequence.RemoveAll()
    $childNodes.Count # Output = 0

    
    # This DOES work; Only difference is '| Sort-Object -Property "Name"'
    <#
    $childNodes = $sequence.ChildNodes | Sort-Object -Property "Name"

    $childNodes.Count # Output = 3
    $sequence.RemoveAll()
    $childNodes.Count # Output = 3
    #>

    $childNodes | ForEach-Object {
        $child = $_
        if ($child.Name -eq "RowId") {
            $sequence.InsertBefore($child, $sequence.FirstChild)

        } else {
            $sequence.AppendChild($child) | out-null
        }
        
    }
}

$rawXML.InnerXml
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 2

原则上回顾一下您已经尝试过的操作:为了$rawXML直接修改存储的 XML DOM,您需要使用[xml]( System.Xml.XmlDocument) .NET 类型自己的方法。

以下简化方法首先找到感兴趣的子元素,然后将.InsertBefore()其移动到顶部(无需在插入之前删除正在移动的元素):

$rawXML.schema.element.complexType.sequence.element.complexType.sequence | ForEach-Object {
  
  # Find the child element whose 'name' attribute is 'RowId'
  $rowIdElem = $_.ChildNodes.Where({ $_.name -eq 'RowId' })[0]

  # Insert the row-ID element before the currently first child element,
  # which effectively moves it to the top.
  $null = $_.InsertBefore($rowIdElem, $_.FirstChildNode)

}
Run Code Online (Sandbox Code Playgroud)

至于你尝试过的

  • $childNodes = $sequence.ChildNodes有效地$childNodes指向存储在元素属性中的子节点的实时列表.ChildNodes(其类型源自System.Xml.XmlNodeList

  • 因此,在调用$sequence.ChildNodes.Clear()删除所有子节点之后, 和$sequence.ChildNodes$childNodes引用一个空集合,即没有任何可枚举的内容,这就是ForEach-Object脚本块 ( $childNodes | ForEach-Object { ... } ) 永远不会被调用的原因。

  • 相比之下,通过调用Sort-Object( $childNodes = $sequence.ChildNode | Sort-Object Name),您可以立即枚举子元素,并将子元素的快照捕获为独立数组(类型为 的常规 PowerShell 数组),稍后将按预期枚举[object[]]元素,导致按自定义顺序重新附加元素的调用按预期工作。ForEach-Object