PowerShell - xpath для имени атрибута XML без учета регистра

У меня есть XML, где имена атрибутов могут быть комбинациями строчных и прописных букв. В приведенном ниже примере атрибут «источник данных» может иметь любое количество строчных и прописных букв.

Мне нужно получить те узлы, где «источником данных» является XML. Я поискал по всему Интернету, но не смог найти для этого никакого решения. Есть несколько примеров для translate (), lower-case (), но они не подходят для моего сценария.

[xml] $GM_ProcessXML =@'
<Process>
    <Parameter Name="Parameter1" Datasource="XML"><![CDATA[Sujeet]]></Parameter>
    <Parameter Name="Parameter2" DataSource="XML"><![CDATA[Padhi]]></Parameter>     
    <Parameter Name="Parameter3" DatASource="XML"><![CDATA[Padhi]]></Parameter>     
    <Parameter Name="Parameter4" datASource="XML"><![CDATA[Padhi]]></Parameter>     
    <Node>
        <Node1 Name="Node1" Datasource="XML"><![CDATA[Sujeet]]></Node1>
        <Node2 Name="Node2" DataSource="XML"><![CDATA[Padhi]]></Node2>      
        <Node3 Name="Node3" DatASource="XML"><![CDATA[Padhi]]></Node3>      
        <Node4 Name="Node4" datASource="XML"><![CDATA[Padhi]]></Node4>    
    </Node>
</Process>
'@

$XPath = "//*[@datasource='XML']"

$Nodes = $GM_ProcessXML.SelectNodes($XPath)

$Nodes

person Sujeet Padhi    schedule 05.06.2020    source источник
comment
В xml 2.0 вы можете сделать это так: // * [нижний регистр (@datasource) = 'xml']   -  person js2010    schedule 05.06.2020


Ответы (3)


  • В XPath 1.0 нет сравнения строк без учета регистра
  • XPath 1.0 - это все, что доступно в .NET.
  • PowerShell полностью основан на .NET.

Так что, на первый взгляд, вам не повезло. Но есть обходной путь, комбинируя код PowerShell и XPath translate().

[xml] $GM_ProcessXML =@'
<Process>
    <Parameter Name="Parameter1" Datasource="XML"><![CDATA[Sujeet]]></Parameter>
    <Parameter Name="Parameter2" DataSource="XML"><![CDATA[Padhi]]></Parameter>     
    <Parameter Name="Parameter3" DatASource="XML"><![CDATA[Padhi]]></Parameter>     
    <Parameter Name="Parameter4" datASource="XML"><![CDATA[Padhi]]></Parameter>     
    <Node>
        <Node1 Name="Node1" Datasource="XML"><![CDATA[Sujeet]]></Node1>
        <Node2 Name="Node2" DataSource="XML"><![CDATA[Padhi]]></Node2>      
        <Node3 Name="Node3" DatASource="XML"><![CDATA[Padhi]]></Node3>      
        <Node4 Name="Node4" datASource="XML"><![CDATA[Padhi]]></Node4>    
    </Node>
</Process>
'@

$attributeName = 'DataSource'
$lc = $attributeName.ToLowerInvariant()    # -> 'datasource'
$uc = $attributeName.ToUpperInvariant()    # -> 'DATASOURCE'

$XPath = "//*[@*[translate(name(), '$uc', '$lc') = '$lc'] = 'XML']"
# ->      //*[@*[translate(name(), 'DATASOURCE', 'datasource') = 'datasource'] = 'XML']

$Nodes = $GM_ProcessXML.SelectNodes($XPath)

Конечно, вы можете написать функцию перевода вручную, особенно в тех случаях, когда переводимая строка жестко запрограммирована.

Но этот подход является общим, поэтому вы также можете использовать его для поиска без учета регистра любого пользовательского / динамического значения, например:

$value = 'sujeet'   # as the user has entered it

$lc = $value.ToLowerInvariant()
$uc = $value.ToUpperInvariant()

$XPath = "//Parameter[translate(., '$uc', '$lc') = '$lc']"
# ->      //Parameter[translate(., 'SUJEET', 'sujeet') = 'sujeet']

Это намного лучше, чем часто предлагают

translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')

поскольку это ограничено предварительно определенными алфавитами и не работает, например, символы с диакритическими знаками, если вы явно не включили их, что быстро становится громоздким.

person Tomalak    schedule 05.06.2020

Вы также можете использовать точечную нотацию без учета регистра, например:

$Nodes = $GM_ProcessXML.Process.Parameter | Where-Object { $_.DAtaSourcE -eq 'xML' }

Это вернет массив из System.Xml.XmlElement узлов

person Theo    schedule 05.06.2020
comment
На самом деле XML намного сложнее, и мне нужны все узлы, где источником данных является XML. Итак, я ищу xpath. Отредактировал XML. @Тео - person Sujeet Padhi; 05.06.2020

Если не считать изменения файлов:

[xml]$GM_ProcessXML = @'
<Process>
    <Parameter Name="Parameter1" Datasource="XML"><![CDATA[Sujeet]]></Parameter>
    <Parameter Name="Parameter2" DataSource="XML"><![CDATA[Padhi]]></Parameter>
    <Parameter Name="Parameter3" DatASource="XML"><![CDATA[Padhi]]></Parameter>
    <Parameter Name="Parameter4" datASource="XML"><![CDATA[Padhi]]></Parameter>
    <Node>
        <Node1 Name="Node1" Datasource="XML"><![CDATA[Sujeet]]></Node1>
        <Node2 Name="Node2" DataSource="XML"><![CDATA[Padhi]]></Node2>
        <Node3 Name="Node3" DatASource="XML"><![CDATA[Padhi]]></Node3>
        <Node4 Name="Node4" datASource="XML"><![CDATA[Padhi]]></Node4>
    </Node>
</Process>
'@ -replace 'datasource','datasource'

$XPath = "//*[@datasource='XML']"

$Nodes = $GM_ProcessXML.SelectNodes($XPath)

$Nodes


Name       datasource #cdata-section
----       ---------- --------------
Parameter1 XML        Sujeet
Parameter2 XML        Padhi
Parameter3 XML        Padhi
Parameter4 XML        Padhi
Node1      XML        Sujeet
Node2      XML        Padhi
Node3      XML        Padhi
Node4      XML        Padhi

person js2010    schedule 05.06.2020
comment
Извините за правку, это было совершенно непреднамеренно. (Я думал, что редактирую свой ответ, я не обращал внимания) - person Tomalak; 05.06.2020
comment
Конечно, выполнение операций поиска и замены в XML опасно (и неправильно), и его следует избегать любой ценой. - person Tomalak; 05.06.2020