Селектор атрибутов Ruby nokogiri в файле XML - PullRequest
0 голосов
/ 08 марта 2019

это XML-файл:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:putResponse
            xmlns:ns1="urn:DmsManagerClient">
            <result xsi:type="xsd:string">
                <?xml version="1.0" encoding="ISO-8859-1"?>
                <MESSAGE ID="11c73b9e-687c-4300-baba-b743c26f7c83" TYPE="CUSDMS">
                    <DELIVERY>
                        <FROM>
                            <SENDER>0072000</SENDER>
                            <SERVICE>eService</SERVICE>
                            <DATE>2019-03-08T12:27:25</DATE>
                        </FROM>
                        <TO>
                            <DEALER DEALERCODE="0072000" MARKETCODE="1000"/>
                        </TO>
                    </DELIVERY>
                    <CONTENT>
                        <dms:ComplexResponse ErrorCode="430" ErrorDescription="null :  PrivacyUE Mancante" Return="false"
                            xmlns:dms="http://dmsmanagerservice">
                            <dms:Element Name="DMSVERSION">2.7</dms:Element>
                        </dms:ComplexResponse>
                    </CONTENT>
                </MESSAGE>
            </result>
        </ns1:putResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Я кодирую с Ruby, и я использовал Nokogiri и метод xpath для экстраполяции "CONTENT" файла

это код:

def extrapolate_error(xml)
  doc = Nokogiri::XML(File.open(xml))
  doc.xpath('//CONTENT')

end

и вот результат:

[#<Nokogiri::XML::Element:0x1c5ba78 name="CONTENT" children=[
   #<Nokogiri::XML::Text:0x1c5b940 "\n">, 
   #<Nokogiri::XML::Element:0x1c5b8bc name="ComplexResponse" namespace=#<Nokogiri::XML::Namespace:0x1c5b88c prefix="dms" href="http://dmsmanagerservice"> 
     attributes=[
       #<Nokogiri::XML::Attr:0x1c5b874 name="ErrorCode" value="430">, 
       #<Nokogiri::XML::Attr:0x1c5b868 name="ErrorDescription" value="null :  PrivacyUE Mancante">, 
       #<Nokogiri::XML::Attr:0x1c5b85c name="Return" value="false">] 
       children=[#<Nokogiri::XML::Text:0x1c5b118 "\n">, 
                 #<Nokogiri::XML::Element:0x1c5b094 name="Element" namespace=#<Nokogiri::XML::Namespace:0x1c5b88c prefix="dms" href="http://dmsmanagerservice"> 
             attributes=[#<Nokogiri::XML::Attr:0x1c5b058 name="Name" value="DMSVERSION">] 
             children=[#<Nokogiri::XML::Text:0x1c5abe4 "2.7">]>, 
                     #<Nokogiri::XML::Text:0x1c5aaac "\n">]>,
                     #<Nokogiri::XML::Text:0x1c5a974 "\n">]>]

Теперь мне нужно войти в него и выбрать некоторые атрибуты.

В конкретном мне это нужно:

name = "ErrorCode" value = "430"

name = "ErrorDescription" value = "null: PrivacyUE Mancante"

Я не знаю, как поступить.Вы можете мне помочь?

1 Ответ

1 голос
/ 08 марта 2019

Следующее должно работать для вас, предполагая, что пространство имен dms всегда одинаково

doc.xpath('//CONTENT/dms:ComplexResponse', dms: 'http://dmsmanagerservice')
    .xpath('@ErrorCode | @ErrorDescription')
    .each_with_object({}) do |e,obj| 
      obj[e.name] = e.text
    end
#=> {"ErrorCode"=>"430", "ErrorDescription"=>"null :  PrivacyUE Mancante"}

Вы уже понимаете, как попали в // CONTENT, поэтому оттуда мы используем dms: ComplexResponse для более глубокой навигации, нотак как это пространство имен, мы должны предоставить ссылку на пространство имен, например dms: 'http://dmsmanagerservice'.

Затем мы выбираем интересующие нас атрибуты @ErrorCode и @ErrorDescription.

В XPath труба | означает UNION (подумайте И), поэтому мы хотим выбрать оба.

Затем мы просто создаем Hash, используя name в качестве ключа и text в качестве значения.

XPath Cheatsheet - Полезный ресурс, есливам нужна дополнительная ссылка

Обновление

Вы спрашивали об условных обозначениях, поэтому я бы предложил

ndoc = Nokogiri::XML(doc)
namespaces = ndoc.collect_namespaces

response = ndoc.xpath("//CONTENT/dms:ComplexResponse", namespaces)

if response.xpath("self::node()[@ErrorCode != '' and @ErrorDescription != '']").any?
  response.xpath("@ErrorCode | @ErrorDescription")
  .each_with_object({}) do |e,obj| 
    obj[e.name] = e.text
  end
else
  response.xpath('dms:Element/@Name | dms:Element/text()',namespaces)
    .each_slice(2)
    .map {|s| s.map(&:text)}.to_h
end

Эта проверка позволяет проверить, есть лиErrorCode и и ErrorDescription, если так, то Hash, как первоначально предлагалось.Если нет, то он возвращает все элементы dms: Elements как Hash, поэтому {"DMSVERSION"=>"2.7"} в этом случае Функциональный пример

...