Skip to content

XML Binding Traits

Matt Muller edited this page Oct 5, 2021 · 5 revisions

This wiki contains a mapping between Smithy XML binding traits and generated Ruby code.

To support these traits, an XML node and parser abstraction is provided in Seahorse.

xmlAttribute trait

Serializes an object property as an XML attribute rather than a nested XML element. XML nodes are given an attributes hash to apply to when serializing.

structure MyStructure {
    @xmlAttribute
    foo: String,

    bar: String,
}

The generated code is:

# builders.rb

class MyStructure
  def self.build(node_name, input:)
    xml = Seahorse::XML::Node.new(node_name)
    xml.attributes['foo'] = input[:foo] unless input[:foo].nil?
    xml << Seahorse::XML::Node.new('bar', input[:bar]) unless input[:bar].nil?
    xml
  end
end

# parsers.rb

class MyStructure
  def self.parse(xml)
    data = Types::MyStructure.new
    data.foo = xml.attributes['foo']
    xml.at('bar') do |node|
      data.bar = node.text
    end
    data
  end
end

xmlFlattened trait

Unwraps the values of a list or map into the containing structure. For the list or map to be flat, the XML nodes are built and added to the top level node instead of under a nested node.

structure Foo {
    @xmlFlattened
    flat: MyList,

    nested: MyList,
}

list MyList {
    member: String,
}

The generated code is:

# builders.rb

class Foo
  def self.build(node_name, input:)
    xml = Seahorse::XML::Node.new(node_name)
    xml << MyList.build('flat', input[:my_list]) unless input[:my_list].nil?
    xml << Seahorse::XML::Node.new('nested', MyList.build('member', input[:my_list])) unless input[:my_list].nil?
    xml
  end
end

class MyList
  def self.build(node_name, list)
    xml = []
    list.each do |element|
      xml << Seahorse::XML::Node.new(node_name, element)
    end
    xml
  end
end

# parsers.rb

class Foo
  def self.parse(xml)
    data = Types::Foo.new
    xml.children('flat') do |children|
      data.flat = MyList.parse(children)
    end
    xml.at('nested') do |node|
      data.nested = MyList.parse(node.children('member'))
    end
    data
  end
end

class MyList
  def self.parse(xml)
    data = []
    xml.each do |node|
      data << node.text
    end
    data
  end
end

xmlName trait

Changes the serialized element or attribute name of a structure, union, or member. When using this trait, the node's name is changed.

structure MyStructure {
    @xmlName("Foo")
    foo: String,

    bar: String,
}

The generated code is:

# builders.rb

class MyStructure
  def self.build(node_name, input:)
    xml = Seahorse::XML::Node.new(node_name)
    xml << Seahorse::XML::Node.new('Foo', input[:foo]) unless input[:foo].nil?
    xml << Seahorse::XML::Node.new('bar', input[:bar]) unless input[:bar].nil?
    xml
  end
end

# parsers.rb

class MyStructure
  def self.parse(xml)
    data = Types::MyStructure.new
    xml.at('Foo') do |node|
      data.foo = node.text
    end
    xml.at('bar') do |node|
      data.bar = node.text
    end
    xml
  end
end

xmlNamespace trait

Adds an XML namespace to an XML element. This trait supports a uri and prefix property. This trait is treated similarly to the xmlAttribute trait, using a key of xmlns:{prefix} and a value of uri.

@xmlNamespace(uri: "http://foo.com")
structure MyStructure {
    foo: String,
    bar: String,
}

The generated code is:

# builders.rb

class MyStructure
  def self.build(node_name, input:)
    xml = Seahorse::XML::Node.new(node_name)
    xml.attributes['xmlns'] = 'http://foo.com'
    xml << Seahorse::XML::Node.new('foo', input[:foo]) unless input[:foo].nil?
    xml << Seahorse::XML::Node.new('bar', input[:bar]) unless input[:bar].nil?
    xml
  end
end