
Security News
Another Round of TEA Protocol Spam Floods npm, But It’s Not a Worm
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.
xml-mapping_extensions
Advanced tools
Additional mapping nodes and other utility code for working with XML::Mapping.
This gem adds two methods, write_xml and parse_xml, to XML mapping instances and classes respectively, to reduce
boilerplate.
The write_xml method supplements [XML::Mapping#save_to_xml] by writing the object out as a String rather than as an REXML::Element.
elem = MyElement.new
elem.attribute = 123
elem.text = 'element text'
elem.children = ['child 1', 'child 2']
puts elem.write_xml
outputs
<my-element attribute='123'>
element text
<child>child 1</child>
<child>child 2</child>
</my-element>
The parse_xml method supplements
XML::Mapping::ClassMethods#load_from_xml
by abstracting away the difference between strings, XML documents, XML elements,
files, and IO-like objects.
my_xml_path = 'my_xml.xml'
my_xml_file = File.new(my_xml_path)
my_xml_string = File.read(my_xml_path)
my_xml_io = StringIO.new(my_xml_string)
my_xml_document = REXML::Document.new(my_xml_string)
my_xml_element = my_xml_document.root
# Standard XML::Mapping load_from_xml method
elem = MyXMLClass.load_from_xml(my_xml_element)
# parse_xml equivalent
[my_xml_file, my_xml_string, my_xml_io, my_xml_document, my_xml_element].each do |xml_source|
expect(MyXMLClass.parse_xml(xml_source)).to eq(elem) # assuming MyXMLClass implements ==
end
Both write_xml and parse_xml accept an options hash, to be passed on to save_to_xml or load_from_xml,
respectively:
elem = MyXMLClass.parse(my_xml_string, { mapping: :alternate })
new_xml_string = elem.write_xml({ mapping: :alternate })
To create a custom node type, require xml/mapping_extensions and extend one of
the abstract node classes, or use one of the provided implementations.
NodeBase: Base class for simple single-attribute nodes that
convert XML strings to object values.Note that you must call ::XML::Mapping.add_node_class for your new node class
to be registered with the XML mapping engine.
class LaTeXRationalNode < XML::MappingExtensions::NodeBase
def to_value(xml_text)
match_data = /\\frac\{([0-9.]+)\}\{([0-9.]+)\}/.match(xml_text)
Rational("#{match_data[1]}/#{match_data[2]}")
end
def to_xml_text(value)
"\\frac{#{value.numerator}}{#{value.denominator}}"
end
end
DateNode: maps XML Schema dates to Date objectsTimeNode: ISO 8601 strings to Time objectsUriNode: maps URI strings to URI objectsMimeTypeNode: maps MIME type strings to MIME::Type objectsTypesafeEnumNode: maps XML strings to typesafe_enum valuesrequire 'xml/mapping_extensions'
require 'rexml/document'
class MyElem
include ::XML::Mapping
root_element_name 'my_elem'
date_node :plain_date, 'plain_date'
date_node :zulu_date, 'zulu_date', zulu: true
time_node :time, 'time'
uri_node :uri, 'uri'
mime_type_node :mime_type, 'mime_type'
end
xml_str = '<my_elem>
<plain_date>1999-12-31</plain_date>
<zulu_date>2000-01-01Z</zulu_date>
<time>2000-01-01T02:34:56Z</time>
<uri>http://example.org</uri>
<mime_type>text/plain</mime_type>
</my_elem>'
xml_doc = REXML::Document.new(xml_str)
xml_elem = xml_doc.root
elem = MyElem.load_from_xml(xml_elem)
puts elem.plain_date.inspect
puts elem.zulu_date.inspect
puts elem.time.inspect
puts elem.uri.inspect
puts elem.mime_type.inspect
Outputs
#<Date: 1999-12-31 ((2451544j,0s,0n),+0s,2299161j)>
#<Date: 2000-01-01 ((2451545j,0s,0n),+0s,2299161j)>
2000-01-01 02:34:56 UTC
#<URI::HTTP http://example.org>
#<MIME::Type:0x007f864bdc4f78 @friendly={"en"=>"Text File"}, @system=nil, @obsolete=false, @registered=true, @use_instead=nil, @signature=false, @content_type="text/plain", @raw_media_type="text", @raw_sub_type="plain", @simplified="text/plain", @i18n_key="text.plain", @media_type="text", @sub_type="plain", @docs=[], @encoding="quoted-printable", @extensions=["txt", "asc", "c", "cc", "h", "hh", "cpp", "hpp", "dat", "hlp", "conf", "def", "doc", "in", "list", "log", "markdown", "md", "rst", "text", "textile"], @references=["IANA", "RFC2046", "RFC3676", "RFC5147"], @xrefs={"rfc"=>["rfc2046", "rfc3676", "rfc5147"]}>
elem = MyElem.new
elem.plain_date = Date.new(1999, 12, 31)
elem.zulu_date = Date.new(2000, 1, 1)
elem.time = Time.utc(2000, 1, 1, 2, 34, 56)
elem.uri = URI('http://example.org')
elem.mime_type = MIME::Types['text/plain'].first
puts(elem.write_xml)
Outputs:
<my_elem>
<plain_date>1999-12-31</plain_date>
<zulu_date>2000-01-01Z</zulu_date>
<time>2000-01-01T02:34:56Z</time>
<uri>http://example.org</uri>
<mime_type>text/plain</mime_type>
</my_elem>
The Namespace class encapsulates an XML namespace. The Namespaced module extends XML::Mapping to
add a namespace attribute and write the namespace out when saving to XML.
class MyElem
include XML::MappingExtensions::Namespaced # instead of XML::Mapping
namespace Namespace.new(
prefix: 'px',
uri: 'http://example.org/px'
)
root_element_name 'my_elem'
date_node :plain_date, 'plain_date'
date_node :zulu_date, 'zulu_date', zulu: true
time_node :time, 'time'
uri_node :uri, 'uri'
mime_type_node :mime_type, 'mime_type'
end
MyElem.namespace
# => #<XML::MappingExtensions::Namespace:0x007fb1c6b73e80>
The namespace will then be written out when the object is saved to XML:
obj = MyElem.new(...)
obj.namespace = namespace
puts obj.write_xml
<element
xmlns='http://example.org/px/'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://example.org/px.xsd'
attribute='123'>
element text
<child>child 1</child>
<child>child 2</child>
</element>
Setting a prefix attribute on the namespace will set the prefix on each element in the output:
class MyElem
namespace Namespace.new(
prefix: 'px',
uri: 'http://example.org/px',
schema_location: 'http://example.org/px.xsd'
)
end
obj = MyElem.new(...)
obj.namespace = namespace
puts obj.write_xml
<px:element
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://example.org/px.xsd'
xmlns:px='http://example.org/px/'
attribute='123'>
element text
<px:child>child 1</px:child>
<px:child>child 2</px:child>
</px:element>
The XML::Mapping library provides an “alternate mapping” mechanism allowing multiple XML mappings per class. However, it requires each mapping to be exhaustive -- an alternate mapping must redefine all attribute mappings defined in the primary, or else ignore those attributes. Sometimes, however, it is
class ValidatingElement
include ::XML::Mapping
root_element_name 'element'
text_node :name, '@name'
text_node :value, '@value'
use_mapping :strict
numeric_node :value, '@value', writer: proc { |obj, xml| xml.add_attribute('value', Float(obj.value)) }
end
invalid_string = '<element name="abc" value="efg"/>'
ValidatingElement.parse_xml(invalid_string, mapping: :strict)
# ArgumentError: invalid value for Float(): "efg"
elem = ValidatingElement.parse_xml(invalid_string) # OK
# => #<XML::Mapping::ValidatingElement:0x007fa5631ae9c8>
elem.write_xml
# => "<element name='abc' value='efg'/>"
elem.write_xml(mapping: :strict)
# ArgumentError: invalid value for Float(): "efg"
So far, so good; but say we set a valid value and try to output with the
:strict mapping?
elem.value = 123
elem.write_xml(mapping: :strict)
# => <validating-element value='123'/>
Since the :strict mapping doesn't define a root element name, we get the
default (based on the class name), and since it doesn't define a mapping
for the name attribute, we lose that entirely.
But if we add a fallback mapping --
class ValidatingElement
fallback_mapping :strict, :_default
end
elem.write_xml(mapping: :strict)
# => <element value='123' name='abc'/>
-- the :strict mapping now gets the root element name element,
and the name attribute, as defined under the :_default mapping.
FAQs
Unknown package
We found that xml-mapping_extensions demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.

Security News
PyPI adds Trusted Publishing support for GitLab Self-Managed as adoption reaches 25% of uploads

Research
/Security News
A malicious Chrome extension posing as an Ethereum wallet steals seed phrases by encoding them into Sui transactions, enabling full wallet takeover.