Fixed SAX parsing of XML attributes.

This was utterly broken, mainly due to me overlooking it. There are now 2 new
callbacks to handle this properly:

* on_attribute: to handle a single attribute/value pair
* on_attributes: to handle a collection of attributes (as returned by
  on_attribute)

By default on_attribut returns a Hash, on_attributes in turn merges all
attribute hashes into a single one. This ensures that on_element _actually_
receives the attributes as a Hash, instead of an Array with random
nil/XML::Attribute values.
This commit is contained in:
Yorick Peterse committed 2015-03-21 01:22:59 +01:00
1 parent 605d565104
commit d8b9725b82
3 files changed
+165 -45

No files matched your search

+20 -13
View File
@@ -154,15 +154,12 @@ element
# Attributes
attributes
= attributes_ { val[0] }
| _
;
attributes = attributes_ { on_attributes(val[0]) };
attributes_
= attribute attributes
= attribute attributes_
{
val[1].unshift(val[0])
val[1].unshift(val[0]) if val[0]
val[1]
}
| _
@@ -171,9 +168,7 @@ attributes_
attribute
= attribute_name attribute_follow
{
val[0].value = val[1]
val[0]
on_attribute(val[0][1], val[0][0], val[1])
}
;
@@ -183,8 +178,8 @@ attribute_follow
;
attribute_name
= T_ATTR { on_attribute(val[0]) }
| T_ATTR_NS T_ATTR { on_attribute(val[1], val[0]) }
= T_ATTR { [nil, val[0]] }
| T_ATTR_NS T_ATTR
;
# XML declarations
@@ -388,9 +383,21 @@ string_body_follow
##
# @param [String] name
# @param [String] ns_name
# @param [String] value
# @return [Oga::XML::Attribute]
#
def on_attribute(name, ns_name = nil)
return Attribute.new(:namespace_name => ns_name, :name => name)
def on_attribute(name, ns_name = nil, value = nil)
return Attribute.new(
:namespace_name => ns_name,
:name => name,
:value => value
)
end
##
# @param [Array] attrs
#
def on_attributes(attrs)
return attrs
end
}
+50 -1
View File
@@ -17,6 +17,8 @@ module Oga
# * `on_text`
# * `on_element`
# * `on_element_children`
# * `on_attribute`
# * `on_attributes`
# * `after_element`
#
# For example:
@@ -57,6 +59,14 @@ module Oga
# end
# end
#
# ## Attributes
#
# Attributes returned by `on_attribute` are passed as an Hash as the 3rd
# argument of the `on_element` callback. The keys of this Hash are the
# attribute names (optionally prefixed by their namespace) and their values.
# You can overwrite `on_attribute` to control individual attributes and
# `on_attributes` to control the final set.
#
class SaxParser < Parser
##
# @param [Object] handler The SAX handler to delegate callbacks to.
@@ -86,7 +96,7 @@ module Oga
# @see [Oga::XML::Parser#on_element]
# @return [Array]
#
def on_element(namespace, name, attrs = {})
def on_element(namespace, name, attrs = [])
run_callback(:on_element, namespace, name, attrs)
return namespace, name
@@ -101,6 +111,45 @@ module Oga
#
def after_element(namespace_with_name)
run_callback(:after_element, *namespace_with_name)
return
end
##
# Manually overwrite this method since for this one we _do_ want the
# return value so it can be passed to `on_element`.
#
# @see [Oga::XML::Parser#on_attribute]
#
def on_attribute(name, ns = nil, value = nil)
if @handler.respond_to?(:on_attribute)
return run_callback(:on_attribute, name, ns, value)
end
key = ns ? "#{ns}:#{name}" : name
return {key => value}
end
##
# Merges the attributes together into a Hash.
#
# @param [Array] attrs
# @return [Hash]
#
def on_attributes(attrs)
if @handler.respond_to?(:on_attributes)
return run_callback(:on_attributes, attrs)
end
merged = {}
attrs.each do |pair|
# Hash#merge requires an extra allocation, this doesn't.
pair.each { |key, value| merged[key] = value }
end
return merged
end
private