Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Added a method so you can know whether an element is empty #19

Closed
wants to merge 2 commits into from

3 participants

@magec

Hi, working with your library (actually with another one that in turn used yours). I ran into the problem that I needed a way to differenciate
this

from this
value
My first approach was to, instead of returning the attributes as the value when an empty node is found, change it to a "" and monkeypatch it with an attributes method, pretty much the same as done in text nodes. Then I realized that this would break the current code written with the library pretty much more than expected. Either way, I ended up writing a patch that marks as empty the values of the empty nodes so a simple test can be done to overcome the problem then I had without breaking backward compatibility

Thanks for the library, Jose Fernández.

@jnunemaker
Owner

Not sure how I feel about this. Anyone else have any thoughts?

@jnunemaker jnunemaker closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 12, 2011
  1. @JoseCF

    Now the hash values returned from to_hash are tampered with an empty_…

    JoseCF authored
    …node? method that returns whether the node has no children
  2. @JoseCF

    Added tests for commit 12b6405

    JoseCF authored
This page is out of date. Refresh to see the latest.
Showing with 47 additions and 3 deletions.
  1. +20 −2 lib/crack/xml.rb
  2. +27 −1 test/xml_test.rb
View
22 lib/crack/xml.rb
@@ -13,7 +13,7 @@
# This represents the hard part of the work, all I did was change the
# underlying parser.
class REXMLUtilityNode #:nodoc:
- attr_accessor :name, :attributes, :children, :type
+ attr_accessor :name, :attributes, :children, :type, :empty_node
def self.typecasts
@@typecasts
@@ -62,6 +62,7 @@ def initialize(name, normalized_attributes = {})
@attributes = undasherize_keys(attributes)
@children = []
@text = false
+ @empty_node = false
end
def add_node(node)
@@ -69,6 +70,14 @@ def add_node(node)
@children << node
end
+ def emptyze!(obj,value)
+ # Monkeypatch to allow empty_node? in the hash values
+ obj.class.instance_eval do
+ define_method(:empty_node?) { @_empty_node }
+ end
+ obj.instance_eval { @_empty_node = value }
+ end
+
def to_hash
if @type == "file"
f = StringIO.new((@children.first || '').unpack('m').first)
@@ -84,6 +93,7 @@ class << f
t = typecast_value( unnormalize_xml_entities( inner_html ) )
t.class.send(:attr_accessor, :attributes)
t.attributes = attributes
+ emptyze!(t,false)
return { name => t }
else
#change repeating groups into an array
@@ -114,6 +124,8 @@ class << f
out = out.empty? ? nil : out
end
+ emptyze!(out,@empty_node)
+
if @type && out.nil?
{ name => typecast_value(out) }
else
@@ -189,6 +201,7 @@ class XML
def self.parse(xml)
stack = []
parser = REXML::Parsers::BaseParser.new(xml)
+ inner_elements = [0]
while true
event = parser.pull
@@ -199,10 +212,15 @@ def self.parse(xml)
# do nothing
when :start_element
stack.push REXMLUtilityNode.new(event[1], event[2])
+ inner_elements[-1] += 1
+ inner_elements << 0
when :end_element
if stack.size > 1
temp = stack.pop
+ temp.empty_node = true if inner_elements.pop == 0
stack.last.add_node(temp)
+ else
+ stack.last.empty_node = true if inner_elements.pop == 0
end
when :text, :cdata
stack.last.add_node(event[1]) unless event[1].strip.length == 0 || stack.empty?
@@ -211,4 +229,4 @@ def self.parse(xml)
stack.length > 0 ? stack.pop.to_hash : {}
end
end
-end
+end
View
28 test/xml_test.rb
@@ -68,6 +68,32 @@ class XmlTest < Test::Unit::TestCase
Crack::XML.parse(xml).should == hash
end
+
+ context "Using empty_node? method in the output hash" do
+ setup do
+ @data1 = Crack::XML.parse("<a h='s' />")
+ @data2 = Crack::XML.parse("<a><h>s</h></a>")
+ @data3 = Crack::XML.parse("<a><b><c></c></b></a>")
+ end
+
+ should "allow us to differenciate an empty tag with attributes from inner tags with text nodes" do
+ @data1.should == @data2
+ @data1["a"].empty_node?.should_not == @data2["a"].empty_node?
+ end
+
+ should "be done by monkeypatching the hash keys with a method called empty_node? that tells whether the given node was empty" do
+ @data3["a"].methods.should include(:empty_node?)
+ @data3["a"].empty_node?.should == false
+ @data3["a"]["b"].empty_node?.should == false
+ @data3["a"]["b"]["c"].empty_node?.should == true
+ end
+
+ should "be false in text nodes" do
+ @data2["a"]["h"].empty_node?.should == false
+ end
+
+ end
+
context "Parsing xml with text and attributes" do
setup do
@@ -486,4 +512,4 @@ class XmlTest < Test::Unit::TestCase
should "handle an xml string containing a single space" do
Crack::XML.parse(' ').should == {}
end
-end
+end
Something went wrong with that request. Please try again.