diff --git a/README.adoc b/README.adoc
index 24c59102..938bade0 100644
--- a/README.adoc
+++ b/README.adoc
@@ -3916,15 +3916,22 @@ The following class will parse the XML snippet below:
[source,ruby]
----
+class Metadata < Lutaml::Model::Serializable
+ attribute :category, :string
+ attribute :identifier, :string
+end
+
class CustomCeramic < Lutaml::Model::Serializable
attribute :name, :string
attribute :size, :integer
attribute :description, :string
+ attribute :metadata, Metadata
xml do
map_element "Name", to: :name, with: { to: :name_to_xml, from: :name_from_xml }
map_attribute "Size", to: :size, with: { to: :size_to_xml, from: :size_from_xml }
map_content with: { to: :description_to_xml, from: :description_from_xml }
+ map_element :metadata, to: :metadata, with: { to: :metadata_to_xml, from: :metadata_from_xml }
end
def name_to_xml(model, parent, doc)
@@ -3952,6 +3959,26 @@ class CustomCeramic < Lutaml::Model::Serializable
def description_from_xml(model, value)
model.description = value.join.strip.sub(/^XML Description: /, "")
end
+
+ def metadata_to_xml(model, parent, doc)
+ metadata_el = doc.create_element("metadata")
+ category_el = doc.create_element("category")
+ identifier_el = doc.create_element("identifier")
+
+ doc.add_text(category_el, model.metadata.category)
+ doc.add_text(identifier_el, model.metadata.identifier)
+
+ doc.add_element(metadata_el, category_el)
+ doc.add_element(metadata_el, identifier_el)
+ doc.add_element(parent, metadata_el)
+ end
+
+ def metadata_from_xml(model, value)
+ model.metadata ||= Metadata.new
+
+ model.metadata.category = value["elements"]["category"].text
+ model.metadata.identifier = value["elements"]["identifier"].text
+ end
end
----
@@ -3960,6 +3987,10 @@ end
XML Masterpiece: Vase
XML Description: A beautiful ceramic vase
+
+ Metadata
+ 123
+
----
@@ -3971,13 +4002,180 @@ end
@name="Masterpiece: Vase",
@ordered=nil,
@size=12,
- @description="A beautiful ceramic vase">
-> puts CustomCeramic.new(name: "Vase", size: 12, description: "A beautiful vase").to_xml
+ @description="A beautiful ceramic vase",
+ @metadata=#>
+> puts CustomCeramic.new(name: "Vase", size: 12, description: "A beautiful vase", metadata: Metadata.new(category: "Glaze", identifier: 15)).to_xml
#
# XML Masterpiece: Vase
+#
+# Glaze
+# 15
+#
# XML Description: A beautiful vase
#
----
+
+[source,ruby]
+----
+def custom_method_from_xml(model, value)
+ instance = value.node # Lutaml::Model::XmlAdapter::AdapterElement
+ # OR
+ instance = value.node.adapter_node # Adapter::Element
+
+ xml = instance.to_xml
+end
+----
+
+When building a model from XML in **custom methods**, if the `value` parameter is a `mapping_hash`, then it allows access to the parsed XML structure through `value.node` which can be converted to an XML string using `to_xml`.
+
+NOTE: For `NokogiriAdapter`, we can also call `to_xml` on `value.node.adapter_node`.
+
+[source,ruby]
+----
+> value
+> # {"text"=>["\n ", "\n ", "\n "], "elements"=>{"category"=>{"text"=>"Metadata"}}}
+> value.to_xml
+> # undefined_method `to_xml`
+
+> value.node
+
+# Nokogiri Adapter Node
+
+#,
+# #],
+# @default_namespace=nil,
+# @name="category",
+# @namespace_prefix=nil,
+# @text="Metadata">,
+# #],
+# @default_namespace=nil,
+# @name="metadata",
+# @namespace_prefix=nil,
+# @text="\n Metadata\n ">
+
+# Ox Adapter Node
+
+#],
+# @default_namespace=nil,
+# @name="category",
+# @namespace_prefix=nil,
+# @text="Metadata">],
+# @default_namespace=nil,
+# @name="metadata",
+# @namespace_prefix=nil,
+# @text=nil>
+
+# Oga Adapter Node
+
+# ,
+# #],
+# @default_namespace=nil,
+# @name="category",
+# @namespace_prefix=nil,
+# @text="Metadata">,
+# #],
+# @default_namespace=nil,
+# @name="metadata",
+# @namespace_prefix=nil,
+# @text="\n Metadata\n ">
+
+> value.node.to_xml
+> #Metadata
+----
+====
+
+==== Separate Serialization Model With Custom Methods
+
+[example]
+====
+The following class will parse the XML snippet below:
+
+[source,ruby]
+----
+class CustomModelChild
+ attr_accessor :street, :city
+end
+
+class CustomModelChildMapper < Lutaml::Model::Serializable
+ model CustomModelChild
+
+ attribute :street, Lutaml::Model::Type::String
+ attribute :city, Lutaml::Model::Type::String
+
+ xml do
+ map_element :street, to: :street
+ map_element :city, to: :city
+ end
+end
+
+class CustomModelParentMapper < Lutaml::Model::Serializable
+ attribute :first_name, Lutaml::Model::Type::String
+ attribute :child_mapper, CustomModelChildMapper
+
+ xml do
+ map_element :first_name, to: :first_name
+ map_element :CustomModelChild,
+ with: { to: :child_to_xml, from: :child_from_xml }
+ end
+
+ def child_to_xml(model, parent, doc)
+ child_el = doc.create_element("CustomModelChild")
+ street_el = doc.create_element("street")
+ city_el = doc.create_element("city")
+
+ doc.add_text(street_el, model.child_mapper.street)
+ doc.add_text(city_el, model.child_mapper.city)
+
+ doc.add_element(child_el, street_el)
+ doc.add_element(child_el, city_el)
+ doc.add_element(parent, child_el)
+ end
+
+ def child_from_xml(model, value)
+ model.child_mapper ||= CustomModelChild.new
+
+ model.child_mapper.street = value["elements"]["street"].text
+ model.child_mapper.city = value["elements"]["city"].text
+ end
+end
+----
+
+[source,xml]
+----
+
+ John
+
+ Oxford Street
+ London
+
+
+----
+
+[source,ruby]
+----
+> instance = CustomModelParentMapper.from_xml(xml)
+> #, @first_name="John">
+> CustomModelParentMapper.to_xml(instance)
+> #JohnOxford StreetLondon
+----
+
+NOTE: For **custom models**, `to_xml` is called on the **mapper class**, not on **model instance**.
====