NewsML-G2 for Ruby, built on lutaml-model.

Parse, manipulate, build and serialize IPTC NewsML-G2 (News Architecture) XML with round-trip fidelity. Targets specification version 2.35, power conformance. The model layer is a port of python-newsmlg2, its full test suite is ported, and the official IPTC specification examples and unit-test suite are adopted as compliance fixtures.

Installation

gem "newsmlg2"

Parsing

require "newsmlg2"

doc = Newsmlg2.parse_file("newsitem.xml")     # or Newsmlg2.parse(xml_string)
doc.item                                     # => Newsmlg2::NewsItem (typed)
doc.item.item_meta.item_class.qcode          # => "ninat:text"
doc.item..headlines.first.to_s   # => "Fed to halt QE ..."

Every root element dispatches to its typed model: NewsItem, PackageItem, ConceptItem, KnowledgeItem, CatalogItem, PlanningItem or NewsMessage.

QCodes and catalogs

A document carries its own catalog store: inline <catalog> elements plus <catalogRef> hrefs (the IPTC standard catalogs are bundled, so no network access is needed).

doc.catalog_store.get_scheme_for_alias("ninat").uri
# => "http://cv.iptc.org/newscodes/ninature/"

Newsmlg2.qcode_to_uri("ninat:text", doc)
# => "http://cv.iptc.org/newscodes/ninature/text"
Newsmlg2.uri_to_qcode("http://cv.iptc.org/newscodes/ninature/text", doc)
# => "ninat:text"

Building with the DSL

doc = Newsmlg2.build_news_item(guid: "urn:newsml:acme.com:20260830:00001",
                                lang: "en-GB") do |item|
  item.item_meta do |meta|
    meta.item_class qcode: "ninat:text"
    meta.provider qcode: "nprov:acme" do |p|
      p.name "Acme News Agency"
    end
    meta.version_created "2026-08-30T12:00:00+00:00"
  end
  item. do |cm|
    cm.urgency 2
    cm.headline "Eruption of Icelandic volcano"
    cm.subject qcode: "medtop:20000962" do |s|
      s.name "Volcano"
      s.name "Vulkan", xml_lang: "de"
    end
  end
end

doc.to_xml   # => "<?xml version=\"1.0\" ... ?>"

Builder methods are generated from the model metadata: one per attribute (collections accept repeated calls and singular aliases — subject appends to subjects), keyword arguments become child attributes, plain strings wrap into content-bearing types, and blocks nest.

Serialization

doc.to_xml                 # declaration + canonical 2-space-pretty XML
doc.item.item_meta.to_xml  # any subtree

Serializing an item without a guid raises Newsmlg2::MissingGuidError. The spec’s default attributes (standard, standardversion, conformance, version) are filled in like python-newsmlg2 does.

Architecture

  • lib/newsmlg2/nar_model.rb — the base model: binds the NAR namespace once and provides the declaration DSL (xml_attributes, xml_element, xml_content).

  • lib/newsmlg2/base/ — XSD attribute groups and element groups as mixins (one file per group, ported from python-newsmlg2’s attributegroups.py and the *Group element lists).

  • lib/newsmlg2/types/ — one class per XSD complex type; element names are declared once, at their usage site in the owning model.

  • lib/newsmlg2/items/ — the seven item types, newsMessage, and their structural children (ItemMeta, ContentMeta, PartMeta, GroupSet …).

  • lib/newsmlg2/catalog_store.rb — per-document catalog store and qcode resolution; IPTC catalogs v32–v41 are bundled under lib/newsmlg2/catalogs/.

  • lib/newsmlg2/document.rb — the parse/serialize entry point.

  • lib/newsmlg2/builder.rb — the reflection-driven build DSL.

Compliance

Three test tiers, all green in CI:

  1. python-newsmlg2 test suite port — spec/newsmlg2/*_spec.rb.

  2. IPTC official examples — all 35 example documents (spec/compliance/examples_spec.rb).

  3. IPTC official validation suite — 161 should-pass/should-fail files across all schema versions, ~2100 assertions (spec/compliance/xsd_validation_spec.rb).

IPTC material (examples, schemas, unit-test files) is vendored under spec/fixtures/iptc/ under the IPTC’s CC-BY 4.0 / MIT terms; see spec/fixtures/iptc/README.md.

Development

bundle install
bundle exec rake            # full suite
bundle exec rubocop

License

BSD-2-Clause. NewsML-G2 is a trademark of the IPTC; this library is not affiliated with the IPTC.