Skip to content

Commit

Permalink
Merge branch 'main' of github.com:lutaml/lutaml-model into add_raw_op…
Browse files Browse the repository at this point in the history
…tion_for_attributes
  • Loading branch information
HassanAkbar committed Sep 26, 2024
2 parents 3c23fb0 + fa38aec commit d0c2bce
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 21 deletions.
8 changes: 4 additions & 4 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2024-09-26 09:07:10 UTC using RuboCop version 1.65.1.
# on 2024-09-26 10:56:34 UTC using RuboCop version 1.65.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 101
# Offense count: 102
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# URISchemes: http, https
Expand Down Expand Up @@ -63,15 +63,15 @@ Metrics/CyclomaticComplexity:
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
- 'lib/lutaml/model/xml_adapter/xml_document.rb'

# Offense count: 46
# Offense count: 47
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
Max: 51

# Offense count: 4
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
Metrics/ParameterLists:
Max: 9
Max: 10

# Offense count: 22
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
Expand Down
82 changes: 81 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1462,9 +1462,14 @@ NOTE: The corresponding keyword used by Shale is `receiver:` instead of

==== Attribute serialization with custom methods

===== General

Define custom methods for specific attribute mappings using the `with:` key for
each serialization mapping block for `from` and `to`.


===== XML serialization with custom methods

Syntax:

.XML serialization with custom methods
Expand All @@ -1486,6 +1491,81 @@ xml do
end
----

.Using the `with:` key to define custom serialization methods for XML
[example]
====
The following class will parse the XML snippet below:
[source,ruby]
----
class CustomCeramic < Lutaml::Model::Serializable
attribute :name, :string
attribute :size, :integer
attribute :description, :string
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 }
end
def name_to_xml(model, parent, doc)
el = doc.create_element("Name")
doc.add_text(el, "XML Masterpiece: #{model.name}")
doc.add_element(parent, el)
end
def name_from_xml(model, value)
model.name = value.sub(/^XML Masterpiece: /, "")
end
def size_to_xml(model, parent, doc)
doc.add_attribute(parent, "Size", model.size + 3)
end
def size_from_xml(model, value)
model.size = value.to_i - 3
end
def description_to_xml(model, parent, doc)
doc.add_text(parent, "XML Description: #{model.description}")
end
def description_from_xml(model, value)
model.description = value.join.strip.sub(/^XML Description: /, "")
end
end
----
[source,xml]
----
<CustomCeramic Size="15">
<Name>XML Masterpiece: Vase</Name>
XML Description: A beautiful ceramic vase
</CustomCeramic>
----
[source,ruby]
----
> CustomCeramic.from_xml(xml)
> #<CustomCeramic:0x0000000108d0e1f8
@element_order=["text", "Name", "text", "Size", "text"],
@name="Masterpiece: Vase",
@ordered=nil,
@size=12,
@description="A beautiful ceramic vase",
@validate_on_set=false>
> puts CustomCeramic.new(name: "Vase", size: 12, description: "A beautiful vase").to_xml
# <CustomCeramic Size="15">
# <Name>XML Masterpiece: Vase</Name>
# XML Description: A beautiful vase
# </CustomCeramic>
----
====


===== Key-value data model serialization with custom methods

.Key-value data model serialization with custom methods
[source,ruby]
----
Expand Down Expand Up @@ -1542,7 +1622,7 @@ end


[[attribute-extraction]]
==== Attribute extraction
==== Attribute extraction (for key-value data models only)

NOTE: This feature is for key-value data model serialization only.

Expand Down
6 changes: 6 additions & 0 deletions lib/lutaml/model/mapping_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def initialize(
delegate: nil,
mixed_content: false,
namespace_set: false,
prefix_set: false,
child_mappings: nil
)
@name = name
Expand All @@ -26,6 +27,7 @@ def initialize(
@delegate = delegate
@mixed_content = mixed_content
@namespace_set = namespace_set
@prefix_set = prefix_set
@child_mappings = child_mappings
end

Expand Down Expand Up @@ -74,6 +76,10 @@ def namespace_set?
@namespace_set
end

def prefix_set?
@prefix_set
end

def content_mapping?
name.nil?
end
Expand Down
10 changes: 8 additions & 2 deletions lib/lutaml/model/xml_adapter/builder/nokogiri.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ def add_attribute(element, name, value)
element[name] = value
end

def create_and_add_element(element_name, prefix: nil, attributes: {})
add_namespace_prefix(prefix) if prefix
def create_and_add_element(
element_name,
prefix: (prefix_unset = true
nil),
attributes: {}
)
add_namespace_prefix(prefix)

if block_given?
public_send(element_name, attributes) do
xml.parent.namespace = nil if prefix.nil? && !prefix_unset
yield(self)
end
else
Expand Down
30 changes: 19 additions & 11 deletions lib/lutaml/model/xml_adapter/xml_document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,29 @@ def add_to_xml(xml, element, prefix, value, options = {})
value,
options.merge({ rule: rule, attribute: attribute }),
)
else
elsif rule.prefix_set?
xml.create_and_add_element(rule.name, prefix: prefix) do
if !value.nil?
serialized_value = attribute.type.serialize(value)
add_value(xml, value, attribute)
end
else
xml.create_and_add_element(rule.name) do
add_value(xml, value, attribute)
end
end
end

if attribute.type == Lutaml::Model::Type::Hash
serialized_value.each do |key, val|
xml.create_and_add_element(key) do |element|
element.text(val)
end
end
else
xml.add_text(xml, serialized_value)
def add_value(xml, value, attribute)
if !value.nil?
serialized_value = attribute.type.serialize(value)

if attribute.type == Lutaml::Model::Type::Hash
serialized_value.each do |key, val|
xml.create_and_add_element(key) do |element|
element.text(val)
end
end
else
xml.add_text(xml, serialized_value)
end
end
end
Expand Down
8 changes: 6 additions & 2 deletions lib/lutaml/model/xml_mapping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def map_element(
delegate: nil,
namespace: (namespace_set = false
nil),
prefix: nil
prefix: (prefix_set = false
nil)
)
validate!(name, to, with)

Expand All @@ -57,6 +58,7 @@ def map_element(
namespace: namespace,
prefix: prefix,
namespace_set: namespace_set != false,
prefix_set: prefix_set != false,
)
end

Expand All @@ -68,7 +70,8 @@ def map_attribute(
delegate: nil,
namespace: (namespace_set = false
nil),
prefix: nil
prefix: (prefix_set = false
nil)
)
validate!(name, to, with)

Expand All @@ -81,6 +84,7 @@ def map_attribute(
namespace: namespace,
prefix: prefix,
namespace_set: namespace_set != false,
prefix_set: prefix_set != false,
)
end

Expand Down
4 changes: 3 additions & 1 deletion lib/lutaml/model/xml_mapping_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def initialize(
namespace: nil,
prefix: nil,
mixed_content: false,
namespace_set: false
namespace_set: false,
prefix_set: false
)
super(
name,
Expand All @@ -24,6 +25,7 @@ def initialize(
delegate: delegate,
mixed_content: mixed_content,
namespace_set: namespace_set,
prefix_set: prefix_set,
)

@namespace = if namespace.to_s == "inherit"
Expand Down
50 changes: 50 additions & 0 deletions spec/lutaml/model/xml_mapping_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ class Person < Lutaml::Model::Serializable
attribute :name, Lutaml::Model::Type::String
attribute :address, XmlMapping::Address
end

class ChildNamespaceNil < Lutaml::Model::Serializable
attribute :element_default_namespace, :string
attribute :element_nil_namespace, :string
attribute :element_new_namespace, :string

xml do
root "ChildNamespaceNil"
namespace "http://www.omg.org/spec/XMI/20131001", "xmi"

# this will inherit the namespace from the parent i.e <xmi:ElementDefaultNamespace>
map_element "ElementDefaultNamespace", to: :element_default_namespace

# this will have nil namesapce applied i.e <ElementNilNamespace>
map_element "ElementNilNamespace", to: :element_nil_namespace,
prefix: nil,
namespace: nil

# this will have new namespace i.e <new:ElementNewNamespace>
map_element "ElementNewNamespace", to: :element_new_namespace,
prefix: "new",
namespace: "http://www.omg.org/spec/XMI/20161001"
end
end
end

RSpec.describe Lutaml::Model::XmlMapping do
Expand Down Expand Up @@ -132,6 +156,32 @@ class Person < Lutaml::Model::Serializable
end
end

context "with nil element-level namespace" do
let(:expected_xml) do
<<~XML
<xmi:ChildNamespaceNil xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:new="http://www.omg.org/spec/XMI/20161001">
<xmi:ElementDefaultNamespace>Default namespace</xmi:ElementDefaultNamespace>
<ElementNilNamespace>No namespace</ElementNilNamespace>
<new:ElementNewNamespace>New namespace</new:ElementNewNamespace>
</xmi:ChildNamespaceNil>
XML
end

let(:model) do
XmlMapping::ChildNamespaceNil.new(
{
element_default_namespace: "Default namespace",
element_nil_namespace: "No namespace",
element_new_namespace: "New namespace",
},
)
end

it "expect to apply correct namespaces" do
expect(model.to_xml).to be_equivalent_to(expected_xml)
end
end

context "with schemaLocation" do
let(:xml) do
<<~XML
Expand Down

0 comments on commit d0c2bce

Please sign in to comment.