Преобразование XML с пространствами имен в CSV с помощью powershell

У меня есть этот XML-файл:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:BOX xmlns="urn:loc.gov:item" 
         xmlns:ns2="urn:loc.gov:box" 
         xmlns:ns3="http://www.example.com/inverter" 
         xmlns:ns4="urn:loc.gov:xyz">
    <ns3:Item>
        <Description>ITEM1</Description>
        <PackSizeNumeric>6</PackSizeNumeric>
        <ns2:BuyersItemIdentification>
            <ID>75847589</ID>
        </ns2:BuyersItemIdentification>
        <ns2:CommodityClassification>
            <CommodityCode>856952</CommodityCode>
        </ns2:CommodityClassification>
        <ns2:AdditionalItemProperty>
            <Name>Weight</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:AdditionalItemProperty>
            <Name>Tare</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:ManufacturerParty>
            <ns2:PartyIdentification>
                <ID>847532</ID>
            </ns2:PartyIdentification>
        </ns2:ManufacturerParty>
    </ns3:Item>
    <ns3:Item>
        <Description>ITEM2</Description>
        <PackSizeNumeric>10</PackSizeNumeric>
        <ns2:BuyersItemIdentification>
            <ID>9568475</ID>
        </ns2:BuyersItemIdentification>
        <ns2:CommodityClassification>
            <CommodityCode>348454</CommodityCode>
        </ns2:CommodityClassification>
        <ns2:AdditionalItemProperty>
            <Name>Weight</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:AdditionalItemProperty>
            <Name>Tare</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:ManufacturerParty>
            <ns2:PartyIdentification>
                <ID>7542125</ID>
            </ns2:PartyIdentification>
        </ns2:ManufacturerParty>
    </ns3:Item>
</ns3:BOX>

Я пытаюсь преобразовать его в файл CSV.

Я получаю содержимое:

[xml]$inputFile = Get-Content test.xml

Затем я экспортирую в CSV:

$inputfile.BOX.childnodes | Export-Csv "Stsadm-EnumSites.csv" -NoTypeInformation -Delimiter:";" -Encoding:UTF8

Я получаю поля Description и PackSizeNumeric, но не другие поля, которые находятся в:

"Description";"PackSizeNumeric";"BuyersItemIdentification";"CommodityClassification";"AdditionalItemProperty";"ManufacturerParty"
"ITEM1";"6";"System.Xml.XmlElement";"System.Xml.XmlElement";"System.Object[]";"System.Xml.XmlElement"
"ITEM2";"10";"System.Xml.XmlElement";"System.Xml.XmlElement";"System.Object[]";"System.Xml.XmlElement"

Как лучше всего получить поля, содержащиеся в других пространствах имен?

Я хотел бы получить это

"Description";"PackSizeNumeric";"BuyersItemIdentification";"CommodityClassification";"Weight";"Tare";PartyIdentification
"ITEM1";"6";"75847589";"856952";"0";"0";"847532"
"ITEM2";"10";"9568475";"348454";"0";"0";"7542125"

person debharlock    schedule 01.06.2015    source источник
comment
Покажите желаемые результаты.   -  person user4003407    schedule 01.06.2015
comment
я исправил свой вопрос   -  person debharlock    schedule 01.06.2015
comment
Ваша проблема не связана с пространствами имен XML. У Export-Csv проблемы с преобразованием сложных объектов в текст. Это просто совпадение, что все сложные элементы имеют пространство имен в вашем XML.   -  person user4003407    schedule 01.06.2015
comment
да, нужно повторить. однако кажется, что для облегчения итерации вам понадобится менеджер пространства имен   -  person Vincent De Smet    schedule 01.06.2015
comment
@PetSerAl Можете ли вы помочь мне найти правильное направление? Могу ли я попробовать что-то еще?   -  person debharlock    schedule 01.06.2015
comment
все еще работая над этим, вам нужно пройтись по дочерним узлам и получить либо их дочерние узлы, либо внутренний текст, я разместил незавершенный ответ   -  person Vincent De Smet    schedule 01.06.2015


Ответы (2)


Комбинация Select-Object и Select-Xml работает очень хорошо:

$ns = @{
    item="urn:loc.gov:item"
    ns2="urn:loc.gov:box"
    ns3="http://www.example.com/inverter"
    ns4="urn:loc.gov:xyz"
}

$doc = New-Object xml
$doc.Load("test.xml")

$doc.BOX.ChildNodes | Select-Object -Property `
    Description,`
    PackSizeNumeric, `
    @{Name="BuyersItemIdentification_ID"; Expression={$_.BuyersItemIdentification.ID}}, `
    @{Name="CommodityClassification_CommodityCode"; Expression={$_.CommodityClassification.CommodityCode}}, `
    @{Name="Weight"; Expression={Select-Xml -Namespace $ns -Xml $_ -XPath "./ns2:AdditionalItemProperty[item:Name = 'Weight']/item:Value"}}, `
    @{Name="Tare"; Expression={Select-Xml -Namespace $ns -Xml $_ -XPath "./ns2:AdditionalItemProperty[item:Name = 'Tare']/item:Value"}}, `
    @{Name="ManufacturerParty_ID"; Expression={$_.ManufacturerParty.PartyIdentification.ID}} `
| Export-Csv "Stsadm-EnumSites.csv" -NoTypeInformation -Delimiter:";" -Encoding:UTF8

результат (Stsadm-EnumSites.csv)

"Description";"PackSizeNumeric";"BuyersItemIdentification_ID";"CommodityClassification_CommodityCode";"Weight";"Tare";"ManufacturerParty_ID"
"ITEM1";"6";"75847589";"856952";"0";"0";"847532"
"ITEM2";"10";"9568475";"348454";"0";"0";"7542125"
person Tomalak    schedule 01.06.2015
comment
холодные бобы! намного лучше, чем то, что я пытался сделать, перебирая дочерние узлы. - person Vincent De Smet; 01.06.2015
comment
Спасибо @Vincent :) Это все еще кажется немного громоздким, но, по крайней мере, это очень четко с точки зрения именования столбцов и выбора узлов. - person Tomalak; 01.06.2015

Ответ Томалака краток и кажется лучшим решением проблемы.

Я пытался сделать что-то общее, но результат даже не соответствует запрошенному формату (список дополнительных свойств сложно преобразовать общим способом, имена полей неуклюжи). В любом случае, приведенное ниже решение проходит по дереву XML, сглаживая данные. Он не привязан к именам элементов (кроме начального выбора)

Закончив свой общий ответ, я теперь задаюсь вопросом, не лучше ли написать и применить преобразование XSLT.

#[xml]$xml = Get-Content test.xml
#xml to process
$xml = [xml]@"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:BOX xmlns="urn:loc.gov:item" 
         xmlns:ns2="urn:loc.gov:box" 
         xmlns:ns3="http://www.example.com/inverter" 
         xmlns:ns4="urn:loc.gov:xyz">
    <ns3:Item>
        <Description>ITEM1</Description>
        <PackSizeNumeric>6</PackSizeNumeric>
        <ns2:BuyersItemIdentification>
            <ID>75847589</ID>
        </ns2:BuyersItemIdentification>
        <ns2:CommodityClassification>
            <CommodityCode>856952</CommodityCode>
        </ns2:CommodityClassification>
        <ns2:AdditionalItemProperty>
            <Name>Weight</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:AdditionalItemProperty>
            <Name>Tare</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:ManufacturerParty>
            <ns2:PartyIdentification>
                <ID>847532</ID>
            </ns2:PartyIdentification>
        </ns2:ManufacturerParty>
    </ns3:Item>
    <ns3:Item>
        <Description>ITEM2</Description>
        <PackSizeNumeric>10</PackSizeNumeric>
        <ns2:BuyersItemIdentification>
            <ID>9568475</ID>
        </ns2:BuyersItemIdentification>
        <ns2:CommodityClassification>
            <CommodityCode>348454</CommodityCode>
        </ns2:CommodityClassification>
        <ns2:AdditionalItemProperty>
            <Name>Weight</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:AdditionalItemProperty>
            <Name>Tare</Name>
            <Value>0</Value>
        </ns2:AdditionalItemProperty>
        <ns2:ManufacturerParty>
            <ns2:PartyIdentification>
                <ID>7542125</ID>
            </ns2:PartyIdentification>
        </ns2:ManufacturerParty>
    </ns3:Item>
</ns3:BOX>
"@

$nsm = [Xml.XmlNamespaceManager]$xml.NameTable

$nsm.AddNamespace("ns1","urn:loc.gov:item")
$nsm.AddNamespace("ns2","urn:loc.gov:box")
$nsm.AddNamespace("ns3","http://www.example.com/inverter")
$nsm.AddNamespace("ns4","urn:loc.gov:xyz")

#function to recursively flatten xml subtree into a hashtable (passed in)
function flatten-xml {
  param (
    $Parent,
    $Element,
    $Fieldname,
    $HashTable
  )

  if ($parent -eq "") {
    $label = $fieldname
  } else {
    $label = $parent + "_" + $fieldname 
  }

  #write-host "$label is $($element.GetType())"

  if ($element.GetType() -eq [System.Xml.XmlElement]) { 
    #get property fields

    $element | Get-Member | ? { $_.MemberType -eq "Property" } | % {
      #write-host "moving from $label to $($_.Name)"
      flatten-xml -Parent $label -Element $element.($_.Name) -FieldName $_.Name -HashTable $HashTable
    }
  }elseif($element.GetType() -eq [System.Object[]]) { 
    #write-host "$label is an array"
    $i = 0
    $element | % { flatten-xml -Parent $label -Element $_ -FieldName "item$i" -HashTable $HashTable; $i++ }
  }else {
    $HashTable[$label] = $element
  }
 }

#convert the nodecollection returned by xpath query into hashtables and write them out to CSV
$xml.SelectNodes("//ns3:BOX/ns3:Item",$nsm) | % { 
    $element = $_
    $ht = @{}
    $element | Get-Member | ? { $_.MemberType -eq "Property" } | % {
      flatten-xml -Parent "" -Element $element.($_.Name) -FieldName $_.Name -HashTable $ht 
    }

    [PSCustomObject]$ht
}  | Export-Csv "test2.csv" -NoTypeInformation -Delimiter:";" -Encoding:UTF8

Результат:

> gc .\test2.csv

"AdditionalItemProperty_item0_Name";"AdditionalItemProperty_item0_Value";"AdditionalItemProperty_item1_Name";"AdditionalItemProperty_item1_Value";"BuyersItemIdentification_ID";"CommodityClassification_CommodityCode";"Description";"ManufacturerParty_PartyIdentification_ID";"PackSizeNumeric"
"Weight"                            ;"0"                                  ;"Tare"                              ;"0"                                  ;"75847589"                   ;"856952"                               ;"ITEM1"      ;"847532"                                  ;"6"
"Weight"                            ;"0"                                  ;"Tare"                              ;"0"                                  ;"9568475"                    ;"348454"                               ;"ITEM2"      ;"7542125"                                 ;"10"

Ссылки:

person Vincent De Smet    schedule 01.06.2015
comment
Ваше решение более сложное, но интересно для моего исследования xml. Есть две ошибки :-) 1) порядок полей. Description,PackSizeNumeric, ecc ecc 2) вес и тара - это название столбцов :-) - person debharlock; 02.06.2015
comment
Ничего страшного. Что касается вашего комментария: 1. Порядок - кажется, хеш-таблица изменила порядок ключей (имен элементов), я не пытался найти способ соблюдать порядок и 2. Вес и тара - это 2 элемента в массиве, они не столбцы сами по себе в структуре XML, которую вы дали. Это то, что я выделил в своем описании, я чувствую, что вы не можете перенести этот список значений в столбцы в общем виде. - person Vincent De Smet; 02.06.2015