diff --git a/spec/converters/xml_spec.cr b/spec/converters/xml_spec.cr
index 462bf1c..b8906b2 100644
--- a/spec/converters/xml_spec.cr
+++ b/spec/converters/xml_spec.cr
@@ -98,6 +98,15 @@ XML_INLINE_ARRAY_WITHIN_ARRAY = <<-XML
XML
+XML_NAMESPACE_ARRAY = <<-XML
+
+
+ 1
+ 2
+ 3
+
+XML
+
XML_DOCTYPE = <<-XML
@@ -146,6 +155,26 @@ XML_ALL_EMPTY = <<-XML
XML
+XML_NAMESPACE_PREFIXES = <<-XML
+
+
+ foo
+ bar
+
+XML
+
+XML_NESTED_NAMESPACES = <<-XML
+
+
+ herp
+
+
+
+
+
+
+XML
+
describe OQ::Converters::XML do
describe ".deserialize" do
# See https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html
@@ -211,11 +240,9 @@ describe OQ::Converters::XML do
end
describe Object do
- describe "a key/value pair" do
- it "should output correctly" do
- run_binary(%(Fred), args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"person":"Fred"}\n)
- end
+ it "a key/value pair" do
+ run_binary(%(Fred), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"person":"Fred"}\n)
end
end
@@ -227,82 +254,94 @@ describe OQ::Converters::XML do
end
end
- describe "with whitespace" do
- it "should output correctly" do
- run_binary(WITH_WHITESPACE, args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"item":{"flagID":"0","itemID":"0","locationID":"0","ownerID":"0","quantity":"-1","typeID":"0"}}\n)
- end
+ it "with whitespace" do
+ run_binary(WITH_WHITESPACE, args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"item":{"flagID":"0","itemID":"0","locationID":"0","ownerID":"0","quantity":"-1","typeID":"0"}}\n)
end
end
- describe "with the prolog" do
- it "should output correctly" do
- run_binary(%(- 0
), args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"item":{"typeID":"0"}}\n)
- end
+ it "with the prolog" do
+ run_binary(%(- 0
), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"item":{"typeID":"0"}}\n)
end
end
- describe "a simple object" do
- it "should output correctly" do
- run_binary(%(JaneDoe), args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"person":{"firstname":"Jane","lastname":"Doe"}}\n)
- end
+ it "a simple object" do
+ run_binary(%(JaneDoe), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"person":{"firstname":"Jane","lastname":"Doe"}}\n)
end
end
- describe "attributes" do
- it "should output correctly" do
- run_binary(%(JaneDoe), args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"person":{"@id":"1","@foo":"bar","firstname":"Jane","lastname":"Doe"}}\n)
- end
+ it "attributes" do
+ run_binary(%(JaneDoe), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"person":{"@id":"1","@foo":"bar","firstname":"Jane","lastname":"Doe"}}\n)
end
end
- describe "nested objects" do
- it "should output correctly" do
- run_binary(%(JaneDoe15061123 Foo Street), args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"person":{"firstname":"Jane","lastname":"Doe","location":{"zip":"15061","address":"123 Foo Street"}}}\n)
- end
+ it "nested objects" do
+ run_binary(%(JaneDoe15061123 Foo Street), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"person":{"firstname":"Jane","lastname":"Doe","location":{"zip":"15061","address":"123 Foo Street"}}}\n)
end
end
- describe "complex object" do
- it "should output correctly" do
- run_binary(%(24), args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"root":{"x":{"@a":"1","a":"2"},"y":{"@b":"3","#text":"4"}}}\n)
- end
+ it "complex object" do
+ run_binary(%(24), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"root":{"x":{"@a":"1","a":"2"},"y":{"@b":"3","#text":"4"}}}\n)
end
end
- describe "with mixed content" do
- it "with a single #text node" do
- run_binary(%(xz), args: ["-i", "xml", "-c", ".root"]) do |output|
- output.should eq %({"#text":"x","y":"z"}\n)
- end
+ it "with mixed content" do
+ run_binary(%(xz), args: ["-i", "xml", "-c", ".root"]) do |output|
+ output.should eq %({"#text":"x","y":"z"}\n)
end
end
- describe "with an inline array" do
- it "should output correctly" do
- run_binary(XML_INLINE_ARRAY, args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"article":{"@key":"tr/ibm/RJ2144","author":["E. F. Codd","Robert S. Arnold","Jean-Marc Cadiou","Chin-Liang Chang","Nick Roussopoulos"],"title":"RENDEZVOUS Version 1: An Experimental English Language Query Formulation System for Casual Users of Relational Data Bases.","journal":"IBM Research Report","volume":"RJ2144","month":"January","year":"1978","ee":"db/labs/ibm/RJ2144.html","cdrom":"ibmTR/rj2144.pdf"}}\n)
- end
+ it "with an inline array" do
+ run_binary(XML_INLINE_ARRAY, args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"article":{"@key":"tr/ibm/RJ2144","author":["E. F. Codd","Robert S. Arnold","Jean-Marc Cadiou","Chin-Liang Chang","Nick Roussopoulos"],"title":"RENDEZVOUS Version 1: An Experimental English Language Query Formulation System for Casual Users of Relational Data Bases.","journal":"IBM Research Report","volume":"RJ2144","month":"January","year":"1978","ee":"db/labs/ibm/RJ2144.html","cdrom":"ibmTR/rj2144.pdf"}}\n)
end
end
- describe "with a doctype" do
- it "should output correctly" do
- run_binary(XML_DOCTYPE, args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"dblp":{"mastersthesis":{"@key":"ms/Brown92","author":"Kurt P. Brown","title":"PRPL: A Database Workload Specification Language, v1.3.","year":"1992","school":"Univ. of Wisconsin-Madison"}}}\n)
- end
+ it "with a doctype" do
+ run_binary(XML_DOCTYPE, args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"dblp":{"mastersthesis":{"@key":"ms/Brown92","author":"Kurt P. Brown","title":"PRPL: A Database Workload Specification Language, v1.3.","year":"1992","school":"Univ. of Wisconsin-Madison"}}}\n)
end
end
- describe "with CDATA" do
- it "should output correctly" do
- run_binary(XML_CDATA, args: ["-i", "xml", "-c", "."]) do |output|
- output.should eq %({"desc":"Some Description"}\n)
+ it "with CDATA" do
+ run_binary(XML_CDATA, args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"desc":"Some Description"}\n)
+ end
+ end
+
+ it "with a prefixed key" do
+ run_binary(%(bar), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"a:foo":"bar"}\n)
+ end
+ end
+
+ describe "with namespaces" do
+ it "strips prefixes and namespace declarations of a prefixed namespace" do
+ run_binary(%(bar), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"foo":"bar"}\n)
+ end
+ end
+
+ it "strips prefixes and namespace declarations of a multiple namespaces" do
+ run_binary(%(bar), args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"foo":"bar"}\n)
+ end
+ end
+
+ it "skips prefixed elements that cause mixed content" do
+ run_binary(XML_NESTED_NAMESPACES, args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"root":{"foo":{"bar":{"baz":null}}}}\n)
+ end
+ end
+
+ it "strips prefixes of elements" do
+ run_binary(XML_NAMESPACE_PREFIXES, args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"root":{"foo":"foo","bar":"bar"}}\n)
end
end
end
@@ -358,6 +397,14 @@ describe OQ::Converters::XML do
end
end
end
+
+ describe "with namespaces" do
+ it "strips prefixes and namespace declarations" do
+ run_binary(XML_NAMESPACE_ARRAY, args: ["-i", "xml", "-c", "."]) do |output|
+ output.should eq %({"items":{"n:number":["1","2"],"number":"3"}}\n)
+ end
+ end
+ end
end
end
@@ -605,132 +652,128 @@ describe OQ::Converters::XML do
end
end
- describe "object value mixed/nested array values" do
- it "should emit correctly" do
- run_binary(%({"x":[1,[2,[3]]]}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- 1
-
- - 2
- -
-
- 3
-
-
- \n
- XML
- )
- end
+ it "object value mixed/nested array values" do
+ run_binary(%({"x":[1,[2,[3]]]}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ 1
+
+ - 2
+ -
+
- 3
+
+
+ \n
+ XML
+ )
end
end
- describe "object value array primitive values" do
- it "should emit correctly" do
- run_binary(%({"x":[1,2,3]}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- 1
- 2
- 3
- \n
- XML
- )
- end
+ it "object value array primitive values" do
+ run_binary(%({"x":[1,2,3]}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ 1
+ 2
+ 3
+ \n
+ XML
+ )
end
end
end
describe Object do
- describe "simple key/value" do
- it "should output correctly" do
- run_binary(%({"name":"Jim"}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- Jim
- \n
- XML
- )
- end
+ it "simple key/value" do
+ run_binary(%({"name":"Jim"}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ Jim
+ \n
+ XML
+ )
end
end
- describe "nested object" do
- it "should output correctly" do
- run_binary(%({"name":"Jim", "city": {"street":"forbs"}}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- Jim
-
- forbs
-
- \n
- XML
- )
- end
+ it "nested object" do
+ run_binary(%({"name":"Jim", "city": {"street":"forbs"}}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ Jim
+
+ forbs
+
+ \n
+ XML
+ )
end
end
- describe "with an attribute" do
- it "should output correctly" do
- run_binary(%({"name":"Jim", "city": {"@street":"forbs"}}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- Jim
-
- \n
- XML
- )
- end
+ it "with an attribute" do
+ run_binary(%({"name":"Jim", "city": {"@street":"forbs"}}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ Jim
+
+ \n
+ XML
+ )
end
end
- describe "with an attribute and #text" do
- it "should output correctly" do
- run_binary(%({"name":"Jim", "city": {"@street":"forbs", "#text": "Atlantic"}}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- Jim
- Atlantic
- \n
- XML
- )
- end
+ it "with an attribute and #text" do
+ run_binary(%({"name":"Jim", "city": {"@street":"forbs", "#text": "Atlantic"}}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ Jim
+ Atlantic
+ \n
+ XML
+ )
end
end
- describe "with attributes" do
- it "should output correctly" do
- run_binary(%({"name":"Jim", "city": {"@street":"forbs", "@post": 123}}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- Jim
-
- \n
- XML
- )
- end
+ it "with attributes" do
+ run_binary(%({"name":"Jim", "city": {"@street":"forbs", "@post": 123}}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ Jim
+
+ \n
+ XML
+ )
end
end
- describe "with attributes and #text" do
- it "should output correctly" do
- run_binary(%({"name":"Jim", "city": {"@street":"forbs", "@post": 123, "#text": "Atlantic"}}), args: ["-o", "xml", "."]) do |output|
- output.should eq(<<-XML
-
-
- Jim
- Atlantic
- \n
- XML
- )
- end
+ it "with attributes and #text" do
+ run_binary(%({"name":"Jim", "city": {"@street":"forbs", "@post": 123, "#text": "Atlantic"}}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ Jim
+ Atlantic
+ \n
+ XML
+ )
+ end
+ end
+
+ it "with a prefixed key" do
+ run_binary(%({"foo:name":"Jim"}), args: ["-o", "xml", "."]) do |output|
+ output.should eq(<<-XML
+
+
+ Jim
+ \n
+ XML
+ )
end
end
end
diff --git a/src/converters/xml.cr b/src/converters/xml.cr
index 36a159b..7cff36d 100644
--- a/src/converters/xml.cr
+++ b/src/converters/xml.cr
@@ -29,7 +29,7 @@ module OQ::Converters::XML
end
# Otherwise process the node as a key/value pair
- builder.field node.name do
+ builder.field self.normalize_node_name node do
builder.object do
process_children node, builder
end
@@ -61,7 +61,7 @@ module OQ::Converters::XML
end
# Determine how to process a node's children
- node.children.group_by(&.name).each do |name, children|
+ node.children.group_by(&->normalize_node_name(::XML::Node)).each do |name, children|
# Skip non significant whitespace; Skip mixed character input
if children.first.text? && has_nested_elements(node)
# Only emit text content if there is only one child
@@ -95,6 +95,10 @@ module OQ::Converters::XML
node.children.empty? ? nil : node.children.first.content
end
+ private def self.normalize_node_name(node : ::XML::Node) : String
+ (namespace = node.namespace) && (prefix = namespace.prefix.presence) ? "#{prefix}:#{node.name}" : node.name
+ end
+
def self.serialize(input : IO, output : IO, **args) : Nil
json = ::JSON::PullParser.new input
builder = ::XML::Builder.new output