XSLT, RUBY, как вывести имя следующего элемента из корня? - PullRequest
0 голосов
/ 19 июня 2019

Я работаю над сценарием ruby, который использует XSLT для преобразования XML в CSV. Одна из логик моего кода - динамический захват элемента родительского узла после корня, чтобы он мог обрабатывать его как ряд записей в файле CSV. Мне удалось получить то, что я хочу, используя Oxygen для преобразования XML, но я запускаю эту ошибку, используя Nokogiri:

/ Библиотека / Ruby / Gems / 2.3.0 / gems / nokogiri-1.10.3 / lib / nokogiri / xslt.rb: 32: in parse_stylesheet_doc': compilation error: file selectXMLelement.xsl line 5 element stylesheet (RuntimeError) xsl:version: only 1.1 features are supported compilation error: file selectXMLelement.xsl line 8 element value-of xsl:value-of : could not compile select expression 'concat(':',/data:root/*/local-name())' from /Library/Ruby/Gems/2.3.0/gems/nokogiri-1.10.3/lib/nokogiri/xslt.rb:32:in parse ' из /Library/Ruby/Gems/2.3.0/gems/nokogiri-1.10.3/lib/nokogiri/xslt.rb:13:in XSLT' from EXTC-v1.rb:37:in api_component ' из EXTC-v1.rb: 43: в block in <main>' from EXTC-v1.rb:43:in каждый ' из EXTC-v1.rb: 43: в `'

Я хотел бы знать, есть ли способ использовать Nokogiri, чтобы получить то, что я хочу вместо XSLT, и как использовать логику моего скрипта Ruby.

Я пытался использовать этот XSLT:

 <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:data="urn:com.sample/bsvc"
    exclude-result-prefixes="data"
    version="2.0">
    <xsl:output method="text"/>
    <xsl:template match="/">
            <xsl:value-of select="concat(':',/data:root/*[1]/local-name())"/>
    </xsl:template>
</xsl:stylesheet>

пример XML, и я успешно могу вывести то, что хочу из XSLT ": Data_Request", используя Oxygen

<data:root>
    <data:Data_Request>
        <data:name>John Doe</data:name>
        <data:phone>123456776</data:phone>
    </data:Data_Request>
</data:root>

Мой скрипт на Ruby:

def xslt_transform(filename)
  #dir = File.join(Dir.pwd,'/input/')
  xml_str = File.read(filename)
  doc = Nokogiri::XML xml_str
  template = Nokogiri::XSLT(File.open('Remove-CDATA.xsl'))
  transformed_doc = template.transform(doc)

  File.write(filename, transformed_doc)
end

Dir.glob('*.xml').each {|filename| xslt_transform(filename)}
#this is where iam trying to use the XSLT
def api_component(filename)
  xml_str = File.read(filename)
  doc = Nokogiri::XML xml_str
  template = Nokogiri::XSLT(File.open('selectXMLelement.xsl'))
  transformed_doc = template.transform(doc)

  puts filename
end

api_name = Dir.glob('*xml').each {|filename| api_component(filename)}

puts api_name


def xml_to_csv(filename)
  dir = File.join(Dir.pwd,'/input/')
  xml_str = File.read(filename)
  doc     = Nokogiri::XML xml_str
  csv_filename = filename.gsub('.xml','.csv')
  record  = {} # hashes
  keys    = Set.new
  records = [] # array
  csv     = ""

# Returns a new hash created by traversing the hash and its subhashes, 
# executing the given block on the key and value. The block should return a 2-element array of the form [key, value].
  doc.traverse do |node| 
    value = node.text.gsub(/\n +/, '')
      if node.name != "text" # skip these nodes: if class isnt text then skip
        if value.length > 0 # skip empty nodes
          key = node.name.gsub(/wd:/,'').to_sym
          #api_component = doc.xpath('/*/*[1]')
          # if a new and not empty record, add to our records collection
          if key == :Data_Request && !record.empty? #for regular XML parsng, use the request data. For example :Location_Data
            records << record
            record = {}
          elsif key[/^root$|^document$/]
            # neglect these keys
          else
            key = node.name.gsub(/data:/,'').to_sym
            # in case our value is html instead of text
            record[key] = Nokogiri::HTML.parse(value).text
            # add to our key set only if not already in the set
            keys << key
          end
        end
      end
    end



# build our csv
  dir = File.join(Dir.pwd,'/output/')
  File.open('../output/'+csv_filename, 'wb') do |file|
    file.puts %Q{"#{keys.to_a.join('","')}"}
    records.each do |record|
      keys.each do |key|
        file.write %Q{"#{record[key]}",}
      end
      file.write "\n"
    end
    print ''
    print filename+ " is ready!\n"
    print ''
  end
end

Dir.glob('*.xml').each { |filename| xml_to_csv(filename) }

Как вы можете видеть, прямо сейчас я жестко закодировал элемент узла: if key == :Data_Request && !record.empty?

Есть ли способ сделать это с Нокогири? и он может динамически определять все файлы XML в пути чтения? Если нет, то как мне добиться этого с помощью XSLT, встроенной в мой скрипт? Побочный вопрос! Есть ли способ сделать все данные формата как текстовый формат с моим сценарием, чтобы он мог сохранить лидирующие нули? :)

...