Skip to content

Commit

Permalink
Merge pull request #211 from khaeru/issue/210
Browse files Browse the repository at this point in the history
Write SDMX-ML elements in XSD schema order
  • Loading branch information
khaeru authored Dec 13, 2024
2 parents 4116706 + 47ba7a9 commit 4a44cbe
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 3 deletions.
3 changes: 2 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
# -- Project information ---------------------------------------------------------------

project = "sdmx"
copyright = "2014–2024 sdmx1 developers"
copyright = "2014–%Y sdmx1 developers"
author = "sdmx1 developers"


# -- General configuration -------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions doc/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Next release
Previously such invalid references caused a :class:`KeyError`.
Prompted by an example in :ref:`INSEE <INSEE>`.
- Update the base URL of the :ref:`WB <WB>` source to use HTTPS instead of plain HTTP (:pull:`207`).
- Bugfix for writing :class:`.NameableArtefact` to SDMX-ML (:pull:`211`; thanks :gh-user:`3nz01` for :issue:`210`).
Up to v2.19.1, the :xml:`<com:Annotations>` element was written *after* elements such as :xml:`<com:Name>`, which is opposite the order given in the XSD schemas for SDMX-ML.
:mod:`sdmx.reader.xml` tolerates non-standard element order, but some other implementations do not.

v2.19.1 (2024-10-23)
====================
Expand Down
5 changes: 5 additions & 0 deletions sdmx/format/xml/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .common import validate_xml

__all__ = [
"validate_xml",
]
52 changes: 51 additions & 1 deletion sdmx/tests/writer/test_writer_xml.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import io
import logging
from datetime import datetime

import pytest
from lxml import etree

import sdmx
import sdmx.writer.xml
from sdmx import message
from sdmx.model import common
from sdmx.format.xml import validate_xml
from sdmx.model import common, v21
from sdmx.model import v21 as m
from sdmx.model.v21 import DataSet, DataStructureDefinition, Dimension, Key, Observation
from sdmx.writer.xml import writer as XMLWriter
Expand All @@ -17,6 +19,18 @@
# Fixtures


@pytest.fixture
def structure_message() -> message.StructureMessage:
"""A StructureMessage that serializes to XSD-valid SDMX-XML."""
return message.StructureMessage(
header=message.Header(
id="N_A",
prepared=datetime.now(),
sender=common.Agency(id="N_A"),
)
)


@pytest.fixture
def dsd():
dsd = DataStructureDefinition()
Expand Down Expand Up @@ -49,6 +63,42 @@ def dks(dsd):
# Test specific methods associated with specific classes


class TestNameableArtefact:
def test_xsd(self, structure_message):
"""Annotations for a NameableArtefact are output in the correct order.
In https://github.com/khaeru/sdmx/issues/210 it was reported that
<com:Annotations> incorrectly appeared before <com:Name>.
"""
# Common arguments
args = dict(
# Identifiable Artefact
id="FOO",
# Nameable Artefact
name="foo",
description="bar",
# VersionableArtefact
version="1",
# MaintainableArtefact
maintainer=common.Agency(id="N_A"),
is_external_reference=False,
is_final=True,
)
dsd = v21.DataStructureDefinition(**args)
na = v21.DataflowDefinition(
annotations=[common.Annotation(id="baz", text="qux")], # Annotable Artefact
**args, # Identifiable, Nameable, Versionable, Maintainable
structure=dsd, # Dataflow-specific attributes
)
structure_message.dataflow[na.id] = na

# Write to SDMX-ML
buf = io.BytesIO(sdmx.to_xml(structure_message))

# Validate using XSD. Fails with v2.19.1.
assert validate_xml(buf), buf.getvalue().decode()


def test_contact() -> None:
c = m.Contact(
name="John Smith",
Expand Down
2 changes: 1 addition & 1 deletion sdmx/writer/xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def annotable(obj: common.AnnotableArtefact, *args, **kwargs) -> etree._Element:
# Write Annotations
e_anno = Element("com:Annotations", *[writer.recurse(a) for a in obj.annotations])
if len(e_anno):
args = args + (e_anno,)
args = (e_anno,) + args

try:
return Element(tag, *args, **kwargs)
Expand Down

0 comments on commit 4a44cbe

Please sign in to comment.