Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

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

Closed
wants to merge 2 commits into from

2 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.

Jose Fernand... added some commits
Jose Fernandez (magec) Now the hash values returned from to_hash are tampered with an empty_…
…node? method that returns whether the node has no children
12b6405
Jose Fernandez (magec) Added tests for commit 12b6405 3d22ddc
@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. Now the hash values returned from to_hash are tampered with an empty_…

    Jose Fernandez (magec) authored
    …node? method that returns whether the node has no children
  2. Added tests for commit 12b6405

    Jose Fernandez (magec) 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.