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"}] ] } } } }}