Преобразование XML в PSCustomObject

Я пытаюсь преобразовать стандартный XML-документ в тот, который хранится в серии папок, объединить их вместе, чтобы создать автоматическую систему исправлений. Формат документа XML предлагает наилучшее сочетание гибкости и простоты использования. К сожалению, подсистема XML PowerShell чувствительна к регистру после форматирования XML-документа, что может оставить место для ненужных головных болей, поэтому я пытаюсь преобразовать импортированные XML-документы в PSCustomObjects, но я застрял.

Я не могу найти способ заставить его определять, есть ли дочерние узлы для определенного свойства, поэтому я могу повторить через Convert-XMLtoArray снова, чтобы он преобразовал все дочерние узлы XML в PSCustomObjects.

Результат:

Application     Version InstallType Installers
-----------     ------- ----------- ----------
Mozilla Firefox 64.0.2  Install               

Ожидаемый результат:

Application     Version InstallType Installers
-----------     ------- ----------- ----------
Mozilla Firefox 64.0.2  Install     {Windows 10,Windows7...}     

Код:

function Convert-XmltoArray($xml) {
    $Return = New-Object -TypeName 'PSCustomObject'
    $XML | Get-Member -MemberType Property | ForEach {
        $Property = New-Object -TypeName 'PSCustomObject'
        $Name = $_.name
        $Value = $XML.($Name)
        if ($Value.HasChildNodes) {
            foreach ($Child in $Value.ChildNodes) {
                $Return | Add-Member -Type NoteProperty -Name $Child.localname -Value $($Child.'#text')
                #<SomethingHere>
            }
        }
    }
    $Return
}

$Test = [XML]@"
<Package>
    <Application>Java</Application>
    <Version>8.2.9.23</Version>
    <InstallType>Install</InstallType>
    <Installers>
        <Windows10>
            <x86>
                <File1>
                    <FileName>jre-8u201-windows-i586.exe</FileName>
                    <Parameters>/s</Parameters>
                </File1>
            </x86>
            <x64>
                <file1>
                    <FileName>jre-8u201-windows-x64.exe</FileName>
                    <Parameters>/s</Parameters>
                </file1>
            </x64>
            <IA64>
                <File1>
                    <FileName></FileName>
                    <Parameters></Parameters>
                    <CustomSuccessCodes></CustomSuccessCodes>
                    <CustomErrorCodes></CustomErrorCodes>
                </File1>
            </IA64>
        </Windows10>
        <Windows7>
            <x86>
                <File1>
                    <FileName>jre-8u201-windows-i586.exe</FileName>
                    <Parameters>/s</Parameters>
                </File1>
            </x86>
            <x64>
                <file1>
                    <FileName>jre-8u201-windows-x64.exe</FileName>
                    <Parameters>/s</Parameters>
                </file1>
            </x64>
            <IA64>
                <File1>
                    <FileName></FileName>
                    <Parameters></Parameters>
                    <CustomSuccessCodes></CustomSuccessCodes>
                    <CustomErrorCodes></CustomErrorCodes>
                </File1>
            </IA64>
        </Windows7>
    </Installers>
</Package>
"@
$Result = Convert-XMLToArray -xml $test
$Result

Обновлять

Наконец-то выяснилось, что он даже совместим с PSv2.

Function Convert-XMLtoPSObject {
    Param (
        $XML
    )
    $Return = New-Object -TypeName PSCustomObject
    $xml |Get-Member -MemberType Property |Where-Object {$_.MemberType -EQ "Property"} |ForEach {
        IF ($_.Definition -Match "^\bstring\b.*$") {
            $Return | Add-Member -MemberType NoteProperty -Name $($_.Name) -Value $($XML.($_.Name))
        } ElseIf ($_.Definition -Match "^\System.Xml.XmlElement\b.*$") {
            $Return | Add-Member -MemberType NoteProperty -Name $($_.Name) -Value $(Convert-XMLtoPSObject -XML $($XML.($_.Name)))
        } Else {
            Write-Host " Unrecognized Type: $($_.Name)='$($_.Definition)'"
        }
    }
    $Return
}

person Nick W.    schedule 20.01.2019    source источник
comment
я не понимаю, xml уже является объектом. Почему ты бы так поступил?   -  person 4c74356b41    schedule 20.01.2019
comment
Сделайте шаг назад и опишите реальную проблему, которую вы пытаетесь решить, а не то, что вы считаете решением. Как вы думаете, зачем вам это нужно?   -  person Ansgar Wiechers    schedule 20.01.2019
comment
Возможный дубликат преобразования XML в PSObject   -  person Andrei Odegov    schedule 20.01.2019
comment
@ 4c74356b41 Потому что PSCustomObeject имеет значительно большую гибкость, дополнительные свойства (например .count ()) и не чувствителен к регистру.   -  person Nick W.    schedule 21.01.2019
comment
@AnsgarWiechers Мне нужно пройти через все подуровни свойства, чтобы преобразовать все вспомогательные свойства из XML в PSCustomObject.   -  person Nick W.    schedule 21.01.2019
comment
Мне уже было совершенно ясно, что вы пытаетесь сделать. Я спрашивал почему вы пытаетесь это сделать. Чего вы пытаетесь достичь в итоге, то есть что вы хотите сделать с результатом?   -  person Ansgar Wiechers    schedule 21.01.2019
comment
Поскольку PSCustomObeject имеет значительно большую гибкость, дополнительные свойства (например .count ()) и не чувствительны к регистру. PSCustomObject исключительно интуитивно понятен по сравнению с XML-фреймворком .Net, который использует PowerShell.   -  person Nick W.    schedule 21.01.2019
comment
Это все еще не отвечает на мой вопрос. В любом случае я советую вам выбросить свой код и использовать вместо него JSON. Это сделает именно то, о чем вы просите. XML явно не подходит для того, что вы пытаетесь сделать.   -  person Ansgar Wiechers    schedule 21.01.2019
comment
@AnsgarWiechers: Да, я не уверен, что, возможно, так сложно понять в этом. JSON аналогичен, и я думаю, что дал бы мне необходимую гибкость PowerShell, но файлы XML, которые я создаю для импорта, используются администраторами Jr.Sys, поэтому этот формат намного проще.   -  person Nick W.    schedule 21.01.2019
comment
Если вам нужно создать XML, я предлагаю вам начать знакомиться с тем, как XML и инструменты для работы с ним работают. Не уверен, что в этом так сложно понять. Это мой последний ответ.   -  person Ansgar Wiechers    schedule 21.01.2019


Ответы (1)


Вы никогда этого не получите ...

{Windows 10,Windows7...}  

… Судя по опубликованному образцу XML, он не содержит нескольких версий ОС.

Вот примерный подход, который заполнит столбец «Установщики», но основан только на опубликованном вами образце XML.

$Test = [XML]@"
<package>
    <Application>Mozilla Firefox</Application>
    <Version>64.0.2</Version>
    <InstallType>Install</InstallType>
    <Installers>
        <Windows10>
            <x86>
                <File1>
                    <FileName>Firefox_Setup_64.0.2_x86.exe</FileName>
                    <Parameters>/s</Parameters>
                </File1>
            </x86>
            <x64>
                <file1>
                    <FileName>Firefox_Setup_64.0.2_x64.exe</FileName>
                    <Parameters>/s</Parameters>
                </file1>
            </x64>
            <IA64>
                <File1>
                    <FileName></FileName>
                    <Parameters></Parameters>
                    <CustomSuccessCodes></CustomSuccessCodes>
                    <CustomErrorCodes></CustomErrorCodes>
                </File1>
            </IA64>
        </Windows10>
    </Installers>
</package>
"@

function ConvertFrom-XmlPart($xml)
{
    $hash = @{}

    $xml | 
    Get-Member -MemberType Property |
        % {
        $name = $_.Name
        if ($_.Definition.StartsWith("string "))
        {
            $hash.($Name) = $xml.$($Name)
        }
        elseif ($_.Definition.StartsWith("System.Object[] "))
        {
            $obj = $xml.$($Name)
            $hash.($Name) = $($obj | 
            % { $_.tag }) -join "; "
        }
        elseif ($_.Definition.StartsWith("System.Xml"))
        {
            $obj = $xml.$($Name)
            $hash.($Name) = @{}
            if ($obj.HasAttributes)
            {
                $attrName = $obj.Attributes | 
                Select-Object -First 1 | 
                % { $_.Name }

                if ($attrName -eq "tag")
                {
                    $hash.($Name) = $($obj | 
                    % { $_.tag }) -join "; "
                }
                else
                {
                    $hash.($Name) = ConvertFrom-XmlPart $obj
                }
            }
            if ($obj.HasChildNodes)
            {
                $obj.ChildNodes | 
                % { $hash.($Name).($_.Name) = ConvertFrom-XmlPart $($obj.$($_.Name)) }
            }
        }
    }
    return $hash
}

function ConvertFrom-Xml($xml) 
{
    $hash = @{}
    $hash = ConvertFrom-XmlPart($xml)
    return New-Object PSObject -Property $hash
}

ConvertFrom-XmlPart -xml $Test

# Output:
# =======
# Name                           Value
# ----                           -----
# package                        {InstallType, Version, Installers, Application}

# Walking the data
$Test.package

# Output:
# =======
# Application     Version InstallType Installers
# -----------     ------- ----------- ----------
# Mozilla Firefox 64.0.2  Install     Installers


$Test.package.Installers

# Output:
# =======
# Windows10
# ---------
# Windows10
person postanote    schedule 20.01.2019
comment
Вы правы, я к тому же добавил неверный XML. Я обычно загружаю его как внешний файл, но решил, что добавление его в переменную упростит чтение для целей этого поста. - person Nick W.; 21.01.2019
comment
Фактически, при запуске именно этого фрагмента кода он проходит через дочерние узлы, но все значения отображаются пустыми как {} - person Nick W.; 21.01.2019
comment
Пометка этого как ответа, поскольку это помогло мне разобраться. TY. - person Nick W.; 21.01.2019