module MultiXml
def self.parse_binary(binary, entity) #:nodoc:
TODO: Add support for other encodings
def self.parse_binary(binary, entity) #:nodoc: case entity['encoding'] when 'base64' Base64.decode64(binary) else binary end end
def self.parse_file(file, entity)
def self.parse_file(file, entity) f = StringIO.new(Base64.decode64(file)) f.extend(FileLike) f.original_filename = entity['name'] f.content_type = entity['content_type'] f end
def default_parser
if any parsers are already loaded, then checks
have loaded and installed. First checks to see
The default parser based on what you currently
def default_parser return :libxml if defined?(::LibXML) return :nokogiri if defined?(::Nokogiri) REQUIREMENT_MAP.each do |(library, parser)| begin require library return parser rescue LoadError next end end end
def parse(xml, options={})
Options
Parse an XML string into Ruby.
def parse(xml, options={}) xml.strip! begin hash = typecast_xml_value(undasherize_keys(parser.parse(xml))) || {} rescue parser.parse_error => error raise ParseError, error.to_s, error.backtrace end hash = symbolize_keys(hash) if options[:symbolize_keys] hash end
def parser
def parser return @parser if @parser self.parser = self.default_parser @parser end
def parser=(new_parser)
* :nokogiri
* :libxml
Supported by default are:
Set the XML parser utilizing a symbol, string, or class.
def parser=(new_parser) case new_parser when String, Symbol require "multi_xml/parsers/#{new_parser.to_s.downcase}" @parser = MultiXml::Parsers.const_get("#{new_parser.to_s.split('_').map{|s| s.capitalize}.join('')}") when Class, Module @parser = new_parser else raise "Did not recognize your parser specification. Please specify either a symbol or a class." end end
def symbolize_keys(hash)
def symbolize_keys(hash) hash.inject({}) do |result, (key, value)| new_key = case key when String key.to_sym else key end new_value = case value when Hash symbolize_keys(value) else value end result[new_key] = new_value result end end
def typecast_xml_value(value)
def typecast_xml_value(value) case value when Hash if value['type'] == 'array' _, entries = Array.wrap(value.detect{|key, value| key != 'type'}) if entries.blank? || (value.is_a?(Hash) && c = value[CONTENT_ROOT] && c.blank?) [] else case entries when Array entries.map{|value| typecast_xml_value(value)} when Hash [typecast_xml_value(entries)] else raise "can't typecast #{entries.class.name}: #{entries.inspect}" end end elsif value.has_key?(CONTENT_ROOT) content = value[CONTENT_ROOT] if block = PARSING[value['type']] block.arity == 1 ? block.call(content) : block.call(content, value) else content end elsif value['type'] == 'string' && value['nil'] != 'true' '' # blank or nil parsed values are represented by nil elsif value.blank? || value['nil'] == 'true' nil # If the type is the only element which makes it then # this still makes the value nil, except if type is # a XML node(where type['value'] is a Hash) elsif value['type'] && value.size == 1 && !value['type'].is_a?(Hash) nil else xml_value = value.inject({}) do |hash, (key, value)| hash[key] = typecast_xml_value(value) hash end # Turn {:files => {:file => #<StringIO>} into {:files => #<StringIO>} so it is compatible with # how multipart uploaded files from HTML appear xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value end when Array value.map!{|i| typecast_xml_value(i)} value.length > 1 ? value : value.first when String value else raise "can't typecast #{value.class.name}: #{value.inspect}" end end
def undasherize_keys(params)
def undasherize_keys(params) case params when Hash params.inject({}) do |hash, (key, value)| hash[key.to_s.tr('-', '_')] = undasherize_keys(value) hash end when Array params.map{|value| undasherize_keys(value)} else params end end