XML to Hash conversion: Nori drops the attributes of the deepest XML elements XML to Hash conversion: Nori drops the attributes of the deepest XML elements xml xml

XML to Hash conversion: Nori drops the attributes of the deepest XML elements


Nori is not actually dropping the attributes, they are just not being printed.

If you run the ruby script:

require 'nori'data = Nori.new(empty_tag_value: true).parse(<<XML)<?xml version="1.0"?><root>  <objects>    <object>      <fields>        <field name="Name">The name</field>        <field name="Description">A description</field>      </fields>    </object>  </objects></root>XMLfield_list = data['root']['objects']['object']['fields']['field']puts "text: '#{field_list[0]}' data: #{field_list[0].attributes}"puts "text: '#{field_list[1]}' data: #{field_list[1].attributes}"

You should get the output

["The name", "A description"]text: 'The name' data: {"name"=>"Name"}text: 'A description' data: {"name"=>"Description"}

Which clearly shows that the attribute are there, but are not displayed by the inspect method (the p(x) function being the same as puts x.inspect).

You will notice that puts field_list.inspect outputs ["The name", "A description"]. but field_list[0].attributes prints the attribute key and data.

If you would like to have pp display this you can overload the inspect method in the Nori::StringWithAttributes.

class Nori  class StringWithAttributes < String    def inspect      [attributes, String.new(self)].inspect    end  endend

Or if you wanted to change the output you could overload the self.new method to have it return a different data strcture.

class Nori  class MyText < Array    def attributes=(data)      self[1] = data    end    attr_accessor :text    def initialize(text)      self[0] = text      self[1] = {}    end  end  class StringWithAttributes < String    def self.new(x)      MyText.new(x)    end  endend

And access the data as a tuple

puts "text: '#{data['root']['objects']['object']['fields']['field'][0].first}' data: #{ data['root']['objects']['object']['fields']['field'][0].last}"

This would make it so you could have the data as JSON or YAML as the text items would look like arrays with 2 elements.pp also works.

{"root"=>  {"objects"=>    {"object"=>      {"fields"=>        {"field"=>          [["The name", {"name"=>"Name"}],           ["A description", {"name"=>"Description"}]]},       "bob"=>[{"@id"=>"id1"}, {"@id"=>"id2"}],       "bill"=>        [{"p"=>["one", {}], "@id"=>"bid1"}, {"p"=>["two", {}], "@id"=>"bid2"}],       "@id"=>"1"}}}}

This should do what you want.

require 'awesome_print'require 'nori'# Copyright (c) 2016 G. Allen Morris III## Awesome Print is freely distributable under the terms of MIT license.# See LICENSE file or http://www.opensource.org/licenses/mit-license.php#------------------------------------------------------------------------------module AwesomePrint  module Nori    def self.included(base)      base.send :alias_method, :cast_without_nori, :cast      base.send :alias_method, :cast, :cast_with_nori    end    # Add Nori XML Node and NodeSet names to the dispatcher pipeline.    #-------------------------------------------------------------------    def cast_with_nori(object, type)      cast = cast_without_nori(object, type)      if defined?(::Nori::StringWithAttributes) && object.is_a?(::Nori::StringWithAttributes)        cast = :nori_xml_node      end      cast    end    #-------------------------------------------------------------------    def awesome_nori_xml_node(object)      return %Q|["#{object}", #{object.attributes}]|    end  endendAwesomePrint::Formatter.send(:include, AwesomePrint::Nori)data = Nori.new(empty_tag_value: true).parse(<<XML)<?xml version="1.0"?><root>  <objects>    <object>      <fields>        <field name="Name">The name</field>        <field name="Description">A description</field>      </fields>    </object>  </objects></root>XMLap data

as the output is:

{    "root" => {        "objects" => {            "object" => {                "fields" => {                    "field" => [                        [0] ["The name", {"name"=>"Name"}],                        [1] ["A description", {"name"=>"Description"}]                    ]                }            }        }    }}