3

これは私のsample.xmlです:

<?xml version="1.0" encoding="utf-8"?>
<ShipmentRequest>
   <Message>
      <Header>
      <MemberId>MID-0000001</MemberId>    
      <MemberName>Bruce</MemberName>
      <DeliveryId>0000001</DeliveryId>
      <OrderNumber>ON-000000001</OrderNumber>
      <ShipToName>Alan</ShipToName>
      <ShipToZip>123-4567</ShipToZip>
      <ShipToStreet>West</ShipToStreet>
      <ShipToCity>Seatle</ShipToCity>
       <Payments>
        <PayType>Credit Card</PayType>
        <Amount>20</Amount>
      </Payments>
      <Payments>
        <PayType>Points</PayType>
        <Amount>22</Amount>
      </Payments>
      <PayType />
      </Header>
    <Line>
      <LineNumber>3.1</LineNumber>
      <ItemId>A-0000001</ItemId>
      <Description>Apple</Description>
      <Quantity>2</Quantity>
      <UnitCost>5</UnitCost>
    </Line>
    <Line>
      <LineNumber>4.1</LineNumber>
      <ItemId>P-0000001</ItemId>
      <Description>Peach</Description>
      <Quantity>4</Quantity>
      <UnitCost>6</UnitCost>
    </Line>
    <Line>
      <LineNumber>5.1</LineNumber>
      <ItemId>O-0000001</ItemId>
      <Description>Orange</Description>
      <Quantity>2</Quantity>
      <UnitCost>4</UnitCost>
    </Line>
  </Message>
</ShipmentRequest>

そして私のsample.rb:

#!/usr/bin/ruby -w

require 'nokogiri'

doc = Nokogiri::XML(open("sample.xml"))
doc.xpath("//ShipmentRequest").each {
  |node| puts node.text
}

そして、私が得る結果:

MID-0000001    
Bruce
0000001
ON-000000001
Alan
123-4567
West
Seatle

Credit Card
20


Points
22




3.1
A-0000001
Apple
2
5


4.1
P-0000001
Peach
4
6


5.1
O-0000001
Orange
2
4

また、タグ名を出力し、値が空白のタグ/ノードをスキップしたいと思います:

MemberID: MID-0000001

MemberName: Bruce

DeliveryId: 0000001

OrderNumber: ON-000000001

ShipToName: Alan

ShipToZip: 123-4567

ShipToStreet: West

etc...
4

2 に答える 2

9

基本的にすべてのリーフ要素が必要です。それらすべてを 1 つの XPath 式でキャプチャできます。

leaves = doc.xpath('//*[not(*)]')

leaves.each do |node|
  puts "#{node.name}: #{node.text}" unless node.text.empty?
end

出力:

MemberId: MID-0000001
MemberName: Bruce
DeliveryId: 0000001
OrderNumber: ON-000000001
ShipToName: Alan
ShipToZip: 123-4567
ShipToStreet: West
ShipToCity: Seatle
PayType: Credit Card
Amount: 20
PayType: Points
Amount: 22
LineNumber: 3.1
ItemId: A-0000001
Description: Apple
Quantity: 2
UnitCost: 5
LineNumber: 4.1
ItemId: P-0000001
Description: Peach
Quantity: 4
UnitCost: 6
LineNumber: 5.1
ItemId: O-0000001
Description: Orange
Quantity: 2
UnitCost: 4

XPathの説明

XPath//*[not(*)]はすべてのリーフ要素を見つけます。それはどのように行うのですか?それを分解しましょう:

  • この//手段は、ドキュメント全体をスキャンします。
  • *任意の要素を意味するため//*、ドキュメント内のすべての要素に一致します。
  • の部分は[]述語と呼ばれ、前の式を制約します。「そんなこと」のように読んでいます。そのスコープは要素の子であるため、たとえば、子を持つa[b]すべてのa要素を意味しbます。
  • not()単にブール否定なので、「not(*)要素なし」を意味するため、述語では「子要素なし」を意味します。

すべてをまとめると、「子要素を持たないようにドキュメント内のすべての要素」== リーフ要素があります。

別バージョン

コメントでは、@Phrogz が素晴らしい追加を行い、別の述語を追加して、要素が空かどうかをチェックするロジックを XPath 式に移動しました。これには 2 つの利点があります。

  • すべての葉を返してからチェックするわけではないため、パフォーマンスが向上します。これは、大きな文書や空のリーフがたくさんある場合に顕著になることがあります。
  • ワンライナーになります!

puts doc.xpath('//*[not(*)][text()]').map{ |n| "#{n.name}: #{n.text}" }

「子要素を持たないが、少なくとも 1 つの子テキスト ノードを持つすべての要素」を意味します。

于 2013-05-31T15:16:09.143 に答える
0
doc = Nokogiri::XML(File.open("sample.xml"))

doc.xpath("//ShipmentRequest/Message/Header").each do |row|
  row.elements.each do |e|
    next if e.text.to_s.empty? 
    if e.name.match(/Payments/)
      e.elements.each do |ie|
        puts "#{ie.name} : #{ie.text}"
      end      
    else
      puts "#{e.name} : #{e.text}"
    end
  end
end

doc.xpath("//ShipmentRequest/Message/Line").each do |row|
  row.elements.each do |e|
    next if e.text.to_s.empty?
    puts "#{e.name} : #{e.text}"
  end
end

出力

MemberId : MID-0000001
MemberName : Bruce
DeliveryId : 0000001
OrderNumber : ON-000000001
ShipToName : Alan
ShipToZip : 123-4567
ShipToStreet : West
ShipToCity : Seatle
PayType : CreditCard
Amount : 20
PayType : Points
Amount : 22
LineNumber : 3.1
ItemId : A-0000001
Description : Apple
Quantity : 2
UnitCost : 5
LineNumber : 4.1
ItemId : P-0000001
Description : Peach
Quantity : 4
UnitCost : 6
LineNumber : 5.1
ItemId : O-0000001
Description : Orange
Quantity : 2
UnitCost : 4
于 2013-05-31T09:25:14.633 に答える