lib/diaspora_federation/parsers/xml_parser.rb
# frozen_string_literal: true
module DiasporaFederation
module Parsers
# This is a parser of XML serialized object.
# Explanations about the XML data format can be found
# {https://diaspora.github.io/diaspora_federation/federation/xml_serialization.html here}.
# @see https://diaspora.github.io/diaspora_federation/federation/xml_serialization.html XML Serialization
# documentation
class XmlParser < BaseParser
# @see BaseParser#parse
# @param [Nokogiri::XML::Element] root_node root XML node of the XML representation of the entity
# @return [Array[1]] comprehensive data for an entity instantiation
def parse(root_node)
from_xml_sanity_validation(root_node)
hash = root_node.element_children.uniq(&:name).to_h {|child|
property, type = find_property_for(child.name)
if property
value = parse_element_from_node(child.name, type, root_node)
[property, value]
else
[child.name, child.text]
end
}
[hash]
end
private
def find_property_for(xml_name)
class_properties.find {|name, type|
if type.instance_of?(Symbol)
name.to_s == xml_name
elsif type.instance_of?(Array)
type.first.entity_name == xml_name
elsif type.ancestors.include?(Entity)
type.entity_name == xml_name
end
}
end
# @param [String] name property name to parse
# @param [Class, Symbol] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML node to parse
# @return [Object] parsed data
def parse_element_from_node(name, type, root_node)
if type.instance_of?(Symbol)
parse_string_from_node(name, type, root_node)
elsif type.instance_of?(Array)
parse_array_from_node(type.first, root_node)
elsif type.ancestors.include?(Entity)
parse_entity_from_node(type, root_node)
end
end
# Create simple entry in data hash
#
# @param [String] name xml tag to parse
# @param [Class, Symbol] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML root_node to parse
# @return [String] data
def parse_string_from_node(name, type, root_node)
node = root_node.xpath(name.to_s)
parse_string(type, node.first.text) if node.any?
end
# Create an entry in the data hash for the nested entity
#
# @param [Class] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML node to parse
# @return [Entity] parsed child entity
def parse_entity_from_node(type, root_node)
node = root_node.xpath(type.entity_name)
type.from_xml(node.first) if node.any? && node.first.children.any?
end
# Collect all nested children of that type and create an array in the data hash
#
# @param [Class] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML node to parse
# @return [Array<Entity>] array with parsed child entities
def parse_array_from_node(type, root_node)
node = root_node.xpath(type.entity_name)
node.select {|child| child.children.any? }.map {|child| type.from_xml(child) } unless node.empty?
end
def from_xml_sanity_validation(root_node)
raise ArgumentError, "only Nokogiri::XML::Element allowed" unless root_node.instance_of?(Nokogiri::XML::Element)
assert_parsability_of(root_node.name)
end
end
end
end