require "spec_helper"
require "lutaml/model"
require "lutaml/model/xml_adapter/nokogiri_adapter"
require "lutaml/model/xml_adapter/ox_adapter"
module CDATA
class Beta < Lutaml::Model::Serializable
attribute :element1, :string
xml do
root "beta"
map_content to: :element1, cdata: true
end
end
class Alpha < Lutaml::Model::Serializable
attribute :element1, :string
attribute :element2, :string
attribute :element3, :string
attribute :beta, Beta
xml do
root "alpha"
map_element "element1", to: :element1, cdata: false
map_element "element2", to: :element2, cdata: true
map_element "element3", to: :element3, cdata: false
map_element "beta", to: :beta
end
end
class Address < Lutaml::Model::Serializable
attribute :street, :string
attribute :city, :string
attribute :house, :string
attribute :address, Address
xml do
root "address"
map_element "street", to: :street
map_element "city", with: { from: :city_from_xml, to: :city_to_xml }, cdata: true
map_element "house", with: { from: :house_from_xml, to: :house_to_xml }, cdata: false
map_element "address", to: :address
end
def house_from_xml(model, node)
model.house = node
end
def house_to_xml(model, _parent, doc)
doc.create_and_add_element("house") do |element|
element.add_text(element, model.house, cdata: false)
end
end
def city_from_xml(model, node)
model.city = node
end
def city_to_xml(model, _parent, doc)
doc.create_and_add_element("city") do |element|
element.add_text(element, model.city, cdata: true)
end
end
end
class CustomModelChild
attr_accessor :street, :city
end
class CustomModelParent
attr_accessor :first_name, :middle_name, :last_name, :child_mapper
def name
"#{first_name} #{last_name}"
end
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, cdata: true
map_element :city, to: :city, cdata: true
end
end
class CustomModelParentMapper < Lutaml::Model::Serializable
model CustomModelParent
attribute :first_name, Lutaml::Model::Type::String
attribute :middle_name, Lutaml::Model::Type::String
attribute :last_name, Lutaml::Model::Type::String
attribute :child_mapper, CustomModelChildMapper
xml do
root "CustomModelParent"
map_element :first_name, to: :first_name, cdata: true
map_element :middle_name, to: :middle_name, cdata: true
map_element :last_name, to: :last_name, cdata: false
map_element :CustomModelChild, with: { to: :child_to_xml, from: :child_from_xml }, cdata: true
end
def child_to_xml(model, _parent, doc)
doc.create_and_add_element("CustomModelChild") do |child_el|
child_el.create_and_add_element("street") do |street_el|
street_el.add_text(street_el, model.child_mapper.street, cdata: true)
end
child_el.create_and_add_element("city") do |city_el|
city_el.add_text(city_el, model.child_mapper.city, cdata: true)
end
end
end
def child_from_xml(model, value)
model.child_mapper ||= CustomModelChild.new
model.child_mapper.street = value["street"].text
model.child_mapper.city = value["city"].text
end
end
class RootMixedContent < Lutaml::Model::Serializable
attribute :id, :string
attribute :bold, :string, collection: true
attribute :italic, :string, collection: true
attribute :underline, :string
attribute :content, :string
xml do
root "RootMixedContent", mixed: true
map_attribute :id, to: :id
map_element :bold, to: :bold, cdata: true
map_element :italic, to: :italic, cdata: true
map_element :underline, to: :underline, cdata: true
map_content to: :content, cdata: true
end
end
class RootMixedContentNested < Lutaml::Model::Serializable
attribute :id, :string
attribute :data, :string
attribute :content, RootMixedContent
attribute :sup, :string, collection: true
attribute :sub, :string, collection: true
xml do
root "RootMixedContentNested", mixed: true
map_content to: :data, cdata: true
map_attribute :id, to: :id
map_element :sup, to: :sup, cdata: true
map_element :sub, to: :sub, cdata: false
map_element "MixedContent", to: :content
end
end
class DefaultValue < Lutaml::Model::Serializable
attribute :name, :string, default: -> { "Default Value" }
attribute :temperature, :integer, default: -> { 1050 }
attribute :opacity, :string, default: -> { "Opaque" }
attribute :content, :string, default: -> { " " }
xml do
root "DefaultValue"
map_element "name", to: :name, render_default: true, cdata: true
map_element "temperature", to: :temperature, render_default: true, cdata: true
map_element "opacity", to: :opacity, cdata: false, render_default: true
map_content to: :content, cdata: true, render_default: true
end
end
end
RSpec.describe "CDATA" do
let(:parent_mapper) { CDATA::CustomModelParentMapper }
let(:child_mapper) { CDATA::CustomModelChildMapper }
let(:parent_model) { CDATA::CustomModelParent }
let(:child_model) { CDATA::CustomModelChild }
shared_examples "cdata behavior" do |adapter_class|
around do |example|
old_adapter = Lutaml::Model::Config.xml_adapter
Lutaml::Model::Config.xml_adapter = adapter_class
example.run
ensure
Lutaml::Model::Config.xml_adapter = old_adapter
end
context "with CDATA option" do
let(:xml) do
<<~XML.strip