Skip to content

Commit

Permalink
Better handle some cases & Release 1.0.2 (#55)
Browse files Browse the repository at this point in the history
* Better handle mixed content in some cases
* Correctly set node content, even if content is blank
* Add specs to ensure compatibility with XML to JSON conventions
* Release 1.0.2
  • Loading branch information
Blacksmoke16 authored Apr 12, 2020
1 parent 2daadcc commit ee40e9e
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 17 deletions.
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: oq
description: |
A performant, and portable jq wrapper thats facilitates the consumption and output of formats other than JSON; using jq filters to transform the data.
version: 1.0.1
version: 1.0.2

authors:
- George Dietrich <[email protected]>
Expand Down
2 changes: 1 addition & 1 deletion snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: oq
version: '1.0.1'
version: '1.0.2'
summary: A performant, and portable jq wrapper to support formats other than JSON
description: |
A performant, and portable jq wrapper thats facilitates the consumption and output of formats other than JSON; using jq filters to transform the data.
Expand Down
61 changes: 58 additions & 3 deletions spec/converters/xml_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,66 @@ XML_ALL_EMPTY = <<-XML
<one> </one>
<two>
</two>
<three/>
<four></four>
</root>
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
describe "conventions" do
describe "an empty element" do
it "self closing" do
run_binary("<e/>", args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":null}\n)
end
end

it "non self closing" do
run_binary("<e></e>", args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":null}\n)
end
end
end

it "an element with pure text content" do
run_binary("<e>text</e>", args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":"text"}\n)
end
end

it "an empty element with attributes" do
run_binary(%(<e name="value" />), args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":{"@name":"value"}}\n)
end
end

it "an element with pure text content and attributes" do
run_binary(%(<e name="value">text</e>), args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":{"@name":"value","#text":"text"}}\n)
end
end

it "an element containing elements with different names" do
run_binary(%(<e> <a>text</a> <b>text</b> </e>), args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":{"a":"text","b":"text"}}\n)
end
end

it "an element containing elements with identical names" do
run_binary(%(<e> <a>text</a> <a>text</a> </e>), args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":{"a":["text","text"]}}\n)
end
end

it "an element containing elements and contiguous text" do
run_binary(%(<e>text<a>text</a></e>), args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"e":{"#text":"text","a":"text"}}\n)
end
end
end

describe "should raise if invalid" do
it "should output correctly" do
run_binary(%(<root id="1<child/></root>), args: ["-i", "xml", "-c", "."]) do |_, status, error|
Expand All @@ -167,7 +222,7 @@ describe OQ::Converters::Xml do
describe "that has only empty children elements" do
it "should output an object with null values" do
run_binary(XML_ALL_EMPTY, args: ["-i", "xml", "-c", "."]) do |output|
output.should eq %({"root":{"one":null,"two":null}}\n)
output.should eq %({"root":{"one":" ","two":"\\n ","three":null,"four":null}}\n)
end
end
end
Expand Down Expand Up @@ -221,9 +276,9 @@ describe OQ::Converters::Xml do
end

describe "with mixed content" do
it "should output correctly" do
it "with a single #text node" do
run_binary(%(<root>x<y>z</y></root>), args: ["-i", "xml", "-c", ".root"]) do |output|
output.should eq %({"y":"z"}\n)
output.should eq %({"#text":"x","y":"z"}\n)
end
end
end
Expand Down
26 changes: 15 additions & 11 deletions src/converters/xml.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@ module OQ::Converters::Xml
xml.read
end

# TODO: clean up after crystal-lang/crystal#8186 is released
if node = xml.expand
process_element_node node, builder
else
raise XML::Error.new LibXML.xmlGetLastError
end
process_element_node xml.expand, builder
end
end
end

private def self.process_element_node(node : XML::Node, builder : JSON::Builder) : Nil
# If the node doesn't have nested elements nor attributes; just emit a scalar value
return builder.field node.name, get_node_value node if !has_nested_elements(node) && node.attributes.empty?
if !has_nested_elements(node) && node.attributes.empty?
return builder.field node.name, get_node_value node
end

# Otherwise process the node as a key/value pair
builder.field node.name do
Expand Down Expand Up @@ -67,7 +64,14 @@ module OQ::Converters::Xml
# Determine how to process a node's children
node.children.group_by(&.name).each do |name, children|
# Skip non significant whitespace; Skip mixed character input
next if children.first.text? && has_nested_elements(node)
if children.first.text? && has_nested_elements(node)
# Only emit text content if there is only one child
if children.size == 1
builder.field "#text", children.first.content
end

next
end

# Array
if children.size > 1
Expand All @@ -89,12 +93,12 @@ module OQ::Converters::Xml
end

private def self.get_node_value(node : XML::Node) : String?
node.children.empty? || node.children.first.content.blank? ? nil : node.children.first.content
node.children.empty? ? nil : node.children.first.content
end

def self.serialize(input : IO, output : IO, **args) : Nil
json = JSON::PullParser.new(input)
builder = XML::Builder.new(output)
json = JSON::PullParser.new input
builder = XML::Builder.new output
indent, prolog, root, xml_item = self.parse_args(args)

builder.indent = indent
Expand Down
2 changes: 1 addition & 1 deletion src/oq.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require "./converters/*"

# A performant, and portable jq wrapper thats facilitates the consumption and output of formats other than JSON; using jq filters to transform the data.
module OQ
VERSION = "1.0.1"
VERSION = "1.0.2"

# The support formats that can be converted to/from.
enum Format
Expand Down

0 comments on commit ee40e9e

Please sign in to comment.