lib/lutaml/model/serialize.rb in lutaml-model-0.3.10 vs lib/lutaml/model/serialize.rb in lutaml-model-0.3.11
- old
+ new
@@ -9,10 +9,11 @@
require_relative "key_value_mapping"
require_relative "json_adapter"
require_relative "comparable_model"
require_relative "schema_location"
require_relative "validation"
+require_relative "error"
module Lutaml
module Model
module Serialize
include ComparableModel
@@ -37,27 +38,49 @@
end
def model(klass = nil)
if klass
@model = klass
+ add_order_handling_methods_to_model(klass)
else
@model
end
end
+ def add_order_handling_methods_to_model(klass)
+ Utils.add_method_if_not_defined(klass, :ordered=) do |ordered|
+ @ordered = ordered
+ end
+
+ Utils.add_method_if_not_defined(klass, :ordered?) do
+ !!@ordered
+ end
+
+ Utils.add_method_if_not_defined(klass, :element_order=) do |order|
+ @element_order = order
+ end
+
+ Utils.add_method_if_not_defined(klass, :element_order) do
+ @element_order
+ end
+ end
+
+ def cast(value)
+ value
+ end
+
# Define an attribute for the model
def attribute(name, type, options = {})
attr = Attribute.new(name, type, options)
attributes[name] = attr
define_method(name) do
instance_variable_get(:"@#{name}")
end
define_method(:"#{name}=") do |value|
- instance_variable_set(:"@#{name}", value)
- # validate!(name)
+ instance_variable_set(:"@#{name}", attr.cast_value(value))
end
end
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
define_method(format) do |&block|
@@ -72,21 +95,21 @@
define_method(:"from_#{format}") do |data|
adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
doc = adapter.parse(data)
- public_send(:"of_#{format}", doc.to_h)
+ public_send(:"of_#{format}", doc)
end
- define_method(:"of_#{format}") do |hash|
- if hash.is_a?(Array)
- return hash.map do |item|
- apply_mappings(item, format)
+ define_method(:"of_#{format}") do |doc|
+ if doc.is_a?(Array)
+ return doc.map do |item|
+ send(:"of_#{format}", item)
end
end
- apply_mappings(hash, format)
+ apply_mappings(doc.to_h, format)
end
define_method(:"to_#{format}") do |instance|
value = public_send(:"as_#{format}", instance)
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
@@ -183,18 +206,21 @@
end
end
def default_mappings(format)
klass = format == :xml ? XmlMapping : KeyValueMapping
+
klass.new.tap do |mapping|
attributes&.each do |name, attr|
mapping.map_element(
name.to_s,
to: name,
render_nil: attr.render_nil?,
)
end
+
+ mapping.root(to_s.split("::").last) if format == :xml
end
end
def apply_child_mappings(hash, child_mappings)
return hash unless child_mappings
@@ -240,52 +266,51 @@
end
hash
end
+ def valid_rule?(rule)
+ attribute = attribute_for_rule(rule)
+
+ !!attribute || rule.custom_methods[:from]
+ end
+
+ def attribute_for_rule(rule)
+ return attributes[rule.to] unless rule.delegate
+
+ attributes[rule.delegate].type.attributes[rule.to]
+ end
+
def apply_mappings(doc, format, options = {})
instance = options[:instance] || model.new
- return instance if !doc || doc.empty?
+ return instance if Utils.blank?(doc)
return apply_xml_mapping(doc, instance, options) if format == :xml
mappings = mappings_for(format).mappings
mappings.each do |rule|
- attr = if rule.delegate
- attributes[rule.delegate].type.attributes[rule.to]
- else
- attributes[rule.to]
- end
+ raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
- raise "Attribute '#{rule.to}' not found in #{self}" unless attr
+ attr = attribute_for_rule(rule)
value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym)
doc[rule.name] || doc[rule.name.to_sym]
else
attr.default
end
if rule.custom_methods[:from]
- if value && !value.empty?
- value = new.send(rule.custom_methods[:from], instance,
- value)
+ if Utils.present?(value)
+ value = new.send(rule.custom_methods[:from], instance, value)
end
+
next
end
value = apply_child_mappings(value, rule.child_mappings)
value = attr.cast(value, format)
- if rule.delegate
- if instance.public_send(rule.delegate).nil?
- instance.public_send(:"#{rule.delegate}=",
- attributes[rule.delegate].type.new)
- end
- instance.public_send(rule.delegate).public_send(:"#{rule.to}=",
- value)
- else
- instance.public_send(:"#{rule.to}=", value)
- end
+ rule.deserialize(instance, value, attributes, self)
end
instance
end
@@ -311,50 +336,59 @@
namespace: doc["__schema_location"][:namespace],
)
end
mappings.each do |rule|
- attr = attributes[rule.to]
- raise "Attribute '#{rule.to}' not found in #{self}" unless attr
+ raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
- is_content_mapping = rule.name.nil?
-
- value = if is_content_mapping
+ value = if rule.content_mapping?
doc["text"]
else
doc[rule.name.to_s] || doc[rule.name.to_sym]
end
- value = [value].compact if attr.collection? && !value.is_a?(Array)
+ value = normalize_xml_value(value, rule)
+ rule.deserialize(instance, value, attributes, self)
+ end
- if value.is_a?(Array)
- value = value.map do |v|
- v.is_a?(Hash) && !(attr.type <= Serialize) ? v["text"] : v
- end
- elsif !(attr.type <= Serialize) && value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
- value = value["text"]
- end
+ instance
+ end
- unless is_content_mapping
- value = attr.cast(
- value,
- :xml,
- caller_class: self,
- mixed_content: rule.mixed_content,
- )
- end
+ def normalize_xml_value(value, rule)
+ attr = attribute_for_rule(rule)
- if rule.custom_methods[:from]
- new.send(rule.custom_methods[:from], instance, value)
- else
- instance.public_send(:"#{rule.to}=", value)
- end
+ value = [value].compact if attr&.collection? && !value.is_a?(Array)
+
+ value = if value.is_a?(Array)
+ value.map do |v|
+ text_hash?(attr, v) ? v["text"] : v
+ end
+ elsif text_hash?(attr, value)
+ value["text"]
+ else
+ value
+ end
+
+ if attr && !rule.content_mapping?
+ value = attr.cast(
+ value,
+ :xml,
+ caller_class: self,
+ mixed_content: rule.mixed_content,
+ )
end
- instance
+ value
end
+ def text_hash?(attr, value)
+ return false unless value.is_a?(Hash)
+ return value.keys == ["text"] unless attr
+
+ !(attr.type <= Serialize) && attr.type != Lutaml::Model::Type::Hash
+ end
+
def ensure_utf8(value)
case value
when String
value.encode("UTF-8", invalid: :replace, undef: :replace,
replace: "")
@@ -437,10 +471,9 @@
hash[key.to_sym] || hash[key.to_s]
end
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
define_method(:"to_#{format}") do |options = {}|
- validate!
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
representation = if format == :xml
self
else
self.class.hash_representation(self, format,