【问题标题】:Selecting multiple nodes using Nokogiri and an upper ancestor node within a variable使用 Nokogiri 和变量内的上祖先节点选择多个节点
【发布时间】:2017-02-10 06:22:45
【问题描述】:

最近几天,我一直在寻找任何解决方案,以使用 Nokogiri 获取多个节点,以受制于祖先节点中的引用变量。

我需要什么: 实际上我正在收集“Segment”节点的所有“Id”。然后我想用“段”节点收集所有后续的“资源”。为了收集“资源”,我想将“Id”设置为变量。

<CPL>
  <SegmL>
    <Segment>
        <Id>UUID</Id> #UUID as a variable
        <Name>name_01</Name>
        <SeqL>
            <ImageSequence>
                <Id>UUID</Id>
                <Track>UUID</Track>
                    <ResourceList>
                        <Resource> #depending on SegmentId
                            <A>aaa</A>
                            <B>bbb</B>
                            <C>ccc</C>
                            <D>ddd</D>
                        </Resource>
                    </ResourceList>
            </ImageSequence>
            <AudioSequence>
                <Id>UUID</Id>
                <Track>UUID</Track>
                    <ResourceList>
                        <Resource>
                            <A>aaa</A>
                            <B>bbb</B>
                            <C>ccc</C>
                            <D>ddd</D>
                        </Resource>
                    </ResourceList>
            </AudioSequence>
        </SequL>
    </Segment>
    <Segment>
        <Id>UUIDa</Id>
        <Name>name_02</Name>
        <SequL>
            <ImageSequence>
                <Id>UUID</Id>
                <Track>UUID</Track>
                    <ResourceList>
                        <Resource>
                            <A>aaa</A>
                            <B>bbb</B>
                            <C>ccc</C>
                            <D>ddd</D>
                        </Resource>
                    </ResourceList>
            </ImageSequence>
            <AudioSequence>
                <Id>UUID</Id>
                <Track>UUID</Track>
                    <ResourceList>
                        <Resource>
                            <A>aaa</A>
                            <B>bbb</B>
                            <C>ccc</C>
                            <D>ddd</D>
                        </Resource>
                    </ResourceList>
            </AudioSequence>
        </SequL>
    </Segment>
  </SegmL>
</CPL>

使用A = Resource.css("A").text.gsub(/\n/,"")收集的所有资源数据

#first each do
cpls.each_with_index do |(cpl_uuid, mycpl), index|
cpl_filename = mycpl
cpl_file = File.open("#{resource_uri}/#{cpl_filename}")
cpl = Nokogiri::XML( cpl_file ).remove_namespaces!

#get UUID for UUID checks
cpl_uuid = cpl.css("Id").first.text.gsub(/\n/,"")
cpl_root_edit_rate  = cpl.css("EditRate").first.text.gsub(/\s+/, "\/")

  #second each do   
  cpl.css("Segment").each do |s| # loop segment
      cpl_segment_list_uuid = s.css("Id").first.text.gsub(/\n/,"") #uuid of segment list

      #third each do
      cpl.css("Resource").each do |f| #loop resources
          cpl_A = f.css("A").text.gsub(/\n/,"") # uuid of A
          cpl_B = f.css("B").text.gsub(/\n/,"") # uuid of B
      end #third
  end #second
end #first

我的表达式给了我这些存储在数组中的信息:

A = 48000.0 
B = 240000.0 
C = 0.0 
D = 240000.0 

Some functions to calculate an average on the resources.

puts all_arry 

A = 5.0
B = 5.0
C = 5.0
D = 5.0
A = 5.0
B = 5.0
C = 5.0
D = 5.0


=8 values -> only 4 values existing for the exact loop (2 average values per Segment)

目前所有“SegmentId”正在收集所有“资源”

如何将每个Segment Id作为变量准确分配后续资源?

我曾经使用过这段代码,但是循环是空的,因为在“Segment”的“Id”和每个“Resource”“A”、“B”之间有更多的节点......:

if cpl.at("Segment/Id:contains(\"#{cpl_segment_list_uuid}\")")
   cpl.css("Resource").each do |f|
      #collecting resources here for each segmet
   end
end

所有节点都没有属性、id、类等。

愿你能帮我解决我的问题。首先,我会礼貌地感谢您的支持!

2016 年 10 月 7 日更新

我还使用以下表达式为资源上的“每个执行”运行了代码:

expression = "/SegmetList/Segment[Id>cpl_segment_list_uuid]"
cpl.xpath(expression).each do |f|

它运行“每个都做”,但我没有得到内部节点

cpl.css("Segment:contains(\"#{cpl_segment_list_uuid}\") > Resource").each do |f|

同上

如果使用“if”条件,也会出现同样的问题:

if cpl.at("Segment/Id:contains(\"#{cpl_segment_list_uuid}\")").each do|f|
#some code
end

2016/18/10 更新

实际上我得到了正确数量的资源 (4),但仍然没有为每个段分开。所以每个 Segment 中都有相同的四个资源。

为什么我没有得到所有资源的双倍数,是因为我在“Segment”循环中创建了数组。

这是当前代码:

#first each do
cpls.each_with_index do |(cpl_uuid, mycpl), index|
cpl_filename = mycpl
cpl_file = File.open("#{resource_uri}/#{cpl_filename}")
cpl = Nokogiri::XML( cpl_file ).remove_namespaces!

#get UUID for UUID checks
cpl_uuid = cpl.css("Id").first.text.gsub(/\n/,"")
cpl_root_edit_rate  = cpl.css("EditRate").first.text.gsub(/\s+/, "\/")

  #second each do   
  cpl.css("Segment").each do |s| # loop segment
      cpl_segment_list_uuid = s.css("Id").first.text.gsub(/\n/,"") #uuid of segment list
      array_for_resource_data = Array.new

      #third each do
      s.css("Resource").each do |f| #loop resources #all resources
      s.search('//A | //B').each do |f| #selecting only resources "A" and "B"
          cpl_A = f.css("A").text.gsub(/\n/,"") # uuid of A
          cpl_B = f.css("B").text.gsub(/\n/,"") # uuid of B
      end #third
  end #second
end #first

我希望我的更新能给你更多的细节。非常感谢您的帮助和解答!

2016/31/10 更新

段的双重输出的问题是固定的。现在我在段下的每个序列上多了一个循环:

cpl.css("Segment").each do |u|
  segment_list_uuid = u.css("Id").first.text.gsub(/\n/,"")
  sequence_list_uuid_arr = Array.new

    u.xpath("//SequenceList[//*[starts-with(name(),'Sequence')]]").each do |s|
      sequence_list_uuid = s.css("TrackId").first.text#.gsub(/\n/,"") 
      sequence_list_uuid_arr.push(cpl_sequence_list_uuid)

    #following some resource nodes
    s.css("Resource").each do |f|
      asset_uuid = f.css("TrackFileId").text.gsub(/\n/,"") 
      resource_uuid = f.css("Id").text.gsub(/\n/,"") 
      edit_rate = f.css("EditRate").text.gsub(/\s+/, "\/")
      #some more code
    end #resource
  end #sequence list
end #segment

现在我想获取每个唯一序列下的所有不同“资源”。我必须列出所有不同的资源并总结一些收集到的值。

有什么方法可以收集同一个“sequence id”下具有不同值(子节点)的每个资源?目前,我不知道任何解决方案....所以没有可以向您展示的代码,可以部分工作。

“资源”循环的each_with_index 不起作用。

您能否提供一些想法或任何方法来帮助我解决我的新问题?

【问题讨论】:

标签: css ruby variables nokogiri nodes


【解决方案1】:

这是拆分 XML 时的常见问题。编写类似于 XML 中数据布局方式的代码,允许重复的相似数据块。

例如:

require 'nokogiri'

cpl = Nokogiri::XML(<<EOT)
<CPL>
  <SegmL>
    <Segment>
        <Id>UUID</Id> #UUID as a variable
        <Name>name_01</Name>
        <SeqL>
            <ImageSequence>
                <Id>UUID</Id>
                <Track>UUID</Track>
                    <ResourceList>
                        <Resource> #depending on SegmentId
                            <A>aaa</A>
                            <B>bbb</B>
                            <C>ccc</C>
                            <D>ddd</D>
                        </Resource>
                    </ResourceList>
            </ImageSequence>
            <AudioSequence>
                <Id>UUID</Id>
                <Track>UUID</Track>
                    <ResourceList>
                        <Resource>
                            <A>aaa</A>
                            <B>bbb</B>
                            <C>ccc</C>
                            <D>ddd</D>
                        </Resource>
                    </ResourceList>
            </AudioSequence>
        </SequL>
    </Segment>
  </SegmL>
</CPL>
EOT

首先找到包含您要迭代的数据的节点,然后开始下降到该数据:

data = cpl.search('Segment').each_with_object([]) { |segment, ary|
    hash = {}

    hash[:id] = segment.at('Id').text
    hash[:name] = segment.at('Name').text

    image_sequence = segment.at('ImageSequence')
    image_sequence_h = {}
    image_sequence_h[:id] = image_sequence.at('Id').text
    image_sequence_h[:track] = image_sequence.at('Track').text

    image_resources_h = {
        a: image_sequence.at('A').text,
        b: image_sequence.at('B').text,
        c: image_sequence.at('C').text,
        d: image_sequence.at('D').text,
    }

    audio_sequence = segment.at('AudioSequence')
    audio_sequence_h = {}
    audio_sequence_h[:id] = audio_sequence.at('Id').text
    audio_sequence_h[:track] = audio_sequence.at('Track').text

    audio_resources_h = {
        a: audio_sequence.at('A').text,
        b: audio_sequence.at('B').text,
        c: audio_sequence.at('C').text,
        d: audio_sequence.at('D').text,
    }

    image_sequence_h[:resources] = image_resources_h
    audio_sequence_h[:resources] = audio_resources_h

    hash[:image_sequence] = image_sequence_h
    hash[:audio_sequence] = audio_sequence_h

    ary << hash
}

这比我通常写的更冗长,因为我希望步骤更清晰。

最终结果是一个哈希数组:

# => [{:id=>"UUID",
#      :name=>"name_01",
#      :image_sequence=>
#       {:id=>"UUID",
#        :track=>"UUID",
#        :resources=>{:a=>"aaa", :b=>"bbb", :c=>"ccc", :d=>"ddd"}},
#      :audio_sequence=>
#       {:id=>"UUID",
#        :track=>"UUID",
#        :resources=>{:a=>"aaa", :b=>"bbb", :c=>"ccc", :d=>"ddd"}}}]

然后很容易遍历数组并访问单个数据块或数据的单个元素:

data[0][:image_sequence][:id] # => "UUID"
data[0][:audio_sequence][:resources][:d] # => "ddd"

【讨论】:

    【解决方案2】:

    试试

    resource.search('.//A | .//B')
    

    .// 将 xpath 查询锚定在当前元素而不是搜索整个文档。

    例子

    elem = doc.search('ImageSequence').first
    elem.search('//A') # returns all A in the whole document
    elem.search('.//A') # returns all A inside element
    

    【讨论】:

      猜你喜欢
      • 2016-03-10
      • 2015-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-10
      • 1970-01-01
      • 1970-01-01
      • 2012-09-07
      相关资源
      最近更新 更多