diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 678a13c..7d17d00 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- java: [ '11' ]
+ java: [ '17' ]
os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest' ]
steps:
- name: Checkout code
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 838acbe..9eaa8d8 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -46,7 +46,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- java: [ '11' ]
+ java: [ '17' ]
os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest' ]
steps:
- name: Checkout code
@@ -110,7 +110,7 @@ jobs:
- name: Setup java
uses: actions/setup-java@v2
with:
- java-version: 11
+ java-version: 17
distribution: 'temurin'
- name: Publish jars
run: ./gradlew publish
diff --git a/README.md b/README.md
index 46c38c7..ad2ddef 100644
--- a/README.md
+++ b/README.md
@@ -5,20 +5,16 @@ Based on the latest version of the [SDMX 3.0 Specifications](https://sdmx.org/?p
statistical data and metadata among international organizations, central banks, and national statistical institutes.
## Packages
-- `sdmx30-infomodel` module contains java classes which represent SDMX 3.0 Information Model (Structure Artifacts) such as Data Structure Definitions (DSDs), Metadata Structure Definitions (MSDs), Code Lists, Concept Schemes, and Data and Metadata flows and others.
+- `sdmx30-infomodel` module contains java classes which represent SDMX 3.0 Information Model (Structure Artifacts) such as Data Structure Definitions (DSDs), Metadata Structure Definitions (MSDs), Code Lists, Concept Schemes, and Data and Metadata flows and others.
+- `sdmx-json10` module providing serialization to JSON 1.0 format for SDMX 2.1
+- `sdmx-json20` module providing serialization to JSON 2.0 format for SDMX 3.0
+- `sdmx-ml21` module providing serialization to ML 2.1 format for SDMX 2.1
+- `sdmx-ml30` module providing serialization to ML 3.0 format for SDMX 3.0
-## Roadmap
-
-The following modules are currently on project's roadmap for 2023:
-- `sdmx30-serialization` module providing (de)serialization to SDMX-JSON, SDMX-ML and SDMX-CSV formats for data and metadata.
-- `sdmx30-reporting` module with models describing to Data and Metadata Reporting.
-- `sdmx30-client` REST client for SDMX registry.
-
-Listed above is a subject for extension and other modules might be implemented as well.
## Requirements
-- Java JDK 11 and higher
+- Java JDK 17 and higher
## Installation
@@ -28,11 +24,60 @@ Listed above is a subject for extension and other modules might be implemented a
com.epam.jsdmx
sdmx30-infomodel
- 1.0.0
+ 2.0.0
+
+
+#### Gradle:
+ compile(group: 'com.epam.jsdmx', name: 'sdmx30-infomodel', version: '2.0.0')
+
+### sdmx-json10
+
+#### Maven:
+
+ com.epam.jsdmx
+ sdmx-json10
+ 2.0.0
+
+
+#### Gradle:
+ compile(group: 'com.epam.jsdmx', name: 'sdmx-json10', version: '2.0.0')
+
+### sdmx-json20
+
+#### Maven:
+
+ com.epam.jsdmx
+ sdmx-json20
+ 2.0.0
+
+
+#### Gradle:
+ compile(group: 'com.epam.jsdmx', name: 'sdmx-json20', version: '2.0.0')
+
+### sdmx-ml21
+
+#### Maven:
+
+ com.epam.jsdmx
+ sdmx-ml21
+ 2.0.0
+
+
+#### Gradle:
+ compile(group: 'com.epam.jsdmx', name: 'sdmx-ml21', version: '2.0.0')
+
+
+### sdmx-ml30
+
+#### Maven:
+
+ com.epam.jsdmx
+ sdmx-ml30
+ 2.0.0
#### Gradle:
- compile(group: 'com.epam.jsdmx', name: 'sdmx30-infomodel', version: '1.0.0')
+ compile(group: 'com.epam.jsdmx', name: 'sdmx-ml30', version: '2.0.0')
## License
Project is distributed under the Apache License 2.0. See [LICENSE](LICENSE) for more information.
diff --git a/build.gradle b/build.gradle
index 8a736c8..7943072 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,8 +9,8 @@ subprojects {
showViolations = true
}
- sourceCompatibility = 11
- targetCompatibility = 11
+ sourceCompatibility = 17
+ targetCompatibility = 17
ext.isReleaseVersion = !version.endsWith("SNAPSHOT")
@@ -122,6 +122,9 @@ subprojects {
compileOnly("org.projectlombok:lombok:${lombok_version}")
annotationProcessor("org.projectlombok:lombok:${lombok_version}")
+ implementation("org.apache.commons:commons-collections4:${org_apache_commons_commons_collections4_version}")
+ implementation("org.apache.commons:commons-lang3:${org_apache_commons_commons_lang3_version}")
+
testImplementation("org.junit.jupiter:junit-jupiter-api:${jupiter_version}")
testImplementation("org.junit.jupiter:junit-jupiter-params:${jupiter_version}")
testImplementation("org.assertj:assertj-core:${assertj_version}")
diff --git a/gradle.properties b/gradle.properties
index 2ff6da7..0e37e71 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,9 +1,25 @@
group=com.epam.jsdmx
-version=1.0.0-SNAPSHOT
+version=2.0.0-SNAPSHOT
#dependencies
+jupiter_version=5.8.2
lombok_version=1.18.24
org_apache_commons_commons_collections4_version=4.4
-org_apache_commons_commons_lang3_version=3.12.0
-jupiter_version=5.8.2
-assertj_version=3.22.0
\ No newline at end of file
+org_apache_commons_commons_lang3_version=3.18.0
+jupiter_api_version=5.11.4
+jupiter_engine_version=5.11.4
+mockito_version=5.4.0
+json_unit_version=4.1.1
+spring_version=6.2.11
+log4j_version=2.25.3
+sdmx_version=2.0.6
+mapstruct_version=1.5.3.Final
+fasterxml_version=2.18.4
+fasterxml_databind_version=2.18.4
+guava_version=33.0.0-jre
+openc_csv_version=5.12.0
+jsonschema2pojo_version=1.2.2
+junit_version=4.13.2
+assertj_version=3.26.3
+xmlunit_legacy_version=2.10.3
+xml_bind_jaxb_version=3.0.2
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 41d9927..1b33c55 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5c82cb0..260504c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/sdmx-json10/build.gradle b/sdmx-json10/build.gradle
new file mode 100644
index 0000000..bf5ae0f
--- /dev/null
+++ b/sdmx-json10/build.gradle
@@ -0,0 +1,46 @@
+plugins {
+ id 'java'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation project(":sdmx30-serializer")
+ implementation project(":sdmx30-infomodel")
+
+ compileOnly("org.projectlombok:lombok:${lombok_version}")
+ annotationProcessor("org.projectlombok:lombok:${lombok_version}")
+
+ implementation("org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}")
+ implementation("org.apache.logging.log4j:log4j-core:${log4j_version}")
+ implementation("com.epam.deltix:SdmxDataParser:${sdmx_version}")
+
+ implementation("com.fasterxml.jackson.core:jackson-core:${fasterxml_version}")
+ implementation("com.fasterxml.jackson.core:jackson-databind:${fasterxml_databind_version}")
+ implementation("com.google.guava:guava:${guava_version}")
+
+ testImplementation("org.projectlombok:lombok:${lombok_version}")
+ testAnnotationProcessor("org.projectlombok:lombok:${lombok_version}")
+
+ testImplementation("org.jsonschema2pojo:jsonschema2pojo-core:${jsonschema2pojo_version}")
+ testImplementation("org.assertj:assertj-core:${assertj_version}")
+ testImplementation("net.javacrumbs.json-unit:json-unit:${json_unit_version}")
+ testImplementation("net.javacrumbs.json-unit:json-unit-assertj:${json_unit_version}")
+ testImplementation("org.mockito:mockito-core:${mockito_version}")
+
+ testImplementation("com.epam.deltix:SdmxApi:${sdmx_version}")
+ testImplementation("com.epam.deltix:SdmxBeans:${sdmx_version}")
+ testImplementation("com.epam.deltix:SdmxDataParser:${sdmx_version}")
+ testImplementation("com.epam.deltix:SdmxFileQueryProcessor:${sdmx_version}")
+ testImplementation("com.epam.deltix:SdmxQueryBuilder:${sdmx_version}")
+ testImplementation("com.epam.deltix:SdmxSourceUtil:${sdmx_version}")
+ testImplementation("com.epam.deltix:SdmxStructureParser:${sdmx_version}")
+ testImplementation("com.epam.deltix:SdmxStructureRetrieval:${sdmx_version}")
+ testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.3'
+}
+
+test {
+ useJUnitPlatform()
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AgencySchemeWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AgencySchemeWriter.java
new file mode 100644
index 0000000..fe888f7
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AgencySchemeWriter.java
@@ -0,0 +1,42 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+
+import java.io.IOException;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.AgencyScheme;
+import com.epam.jsdmx.infomodel.sdmx30.AgencySchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class AgencySchemeWriter extends MaintainableWriter {
+
+ private final OrganisationWriter organisationWriter;
+
+ public AgencySchemeWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ OrganisationWriter organisationWriter) {
+ super(versionableWriter, linksWriter);
+ this.organisationWriter = organisationWriter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator jsonGenerator, AgencyScheme artefact) throws IOException {
+ AgencySchemeImpl agencyScheme = (AgencySchemeImpl) artefact;
+ agencyScheme.setId(StructureUtils.AGENCIES_ID);
+ super.writeFields(jsonGenerator, agencyScheme);
+ jsonGenerator.writeBooleanField(StructureUtils.IS_PARTIAL, agencyScheme.isPartial());
+ organisationWriter.writeOrganisation(jsonGenerator, artefact.getItems(), StructureUtils.AGENCIES);
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getAgencySchemes();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.AGENCY_SCHEMES;
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AnnotableWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AnnotableWriter.java
new file mode 100644
index 0000000..e2e815d
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AnnotableWriter.java
@@ -0,0 +1,44 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.AnnotableArtefact;
+import com.epam.jsdmx.infomodel.sdmx30.Annotation;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
+
+@RequiredArgsConstructor
+public class AnnotableWriter {
+
+ private final LinksWriter linksWriter;
+
+ public void write(JsonGenerator jsonGenerator, T annotableArtefact) throws IOException {
+ write(jsonGenerator, annotableArtefact.getAnnotations());
+ }
+
+ protected void write(JsonGenerator jsonGenerator, List annotations) throws IOException {
+ if (CollectionUtils.isEmpty(annotations)) {
+ return;
+ }
+ jsonGenerator.writeFieldName(StructureUtils.ANNOTATIONS);
+ jsonGenerator.writeStartArray();
+ for (Annotation annotation : annotations) {
+ writeAnnotation(jsonGenerator, annotation);
+ }
+ jsonGenerator.writeEndArray();
+ }
+
+ private void writeAnnotation(JsonGenerator jg, Annotation annotation) throws IOException {
+ jg.writeStartObject();
+ StructureUtils.writeStringField(jg, StructureUtils.ID, annotation.getId());
+ StructureUtils.writeStringField(jg, StructureUtils.TITLE, annotation.getTitle());
+ StructureUtils.writeStringField(jg, StructureUtils.TYPE, annotation.getType());
+ StructureUtils.writeStringField(jg, StructureUtils.TEXT, (annotation.getText() != null) ? annotation.getText().getForDefaultLocale() : null);
+ StructureUtils.writeInternationalString(jg, annotation.getText(), StructureUtils.TEXTS);
+ linksWriter.writeLinks(jg, annotation.getUrl());
+ jg.writeEndObject();
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AttributeListWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AttributeListWriter.java
new file mode 100644
index 0000000..61f1516
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/AttributeListWriter.java
@@ -0,0 +1,111 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static com.epam.jsdmx.json10.structure.writer.StructureUtils.writeStringField;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.AttributeDescriptor;
+import com.epam.jsdmx.infomodel.sdmx30.AttributeDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.AttributeRelationship;
+import com.epam.jsdmx.infomodel.sdmx30.DataAttribute;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionRelationship;
+import com.epam.jsdmx.infomodel.sdmx30.GroupRelationship;
+import com.epam.jsdmx.infomodel.sdmx30.ObservationRelationship;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.EqualsAndHashCode;
+import org.apache.commons.collections4.CollectionUtils;
+
+@EqualsAndHashCode(callSuper = false)
+public class AttributeListWriter {
+
+ private final IdentifiableWriter identifiableWriter;
+ private final ConceptRoleWriter conceptRoleWriter;
+ private final ComponentWriter componentWriter;
+
+ public AttributeListWriter(IdentifiableWriter identifiableWriter,
+ ConceptRoleWriter conceptRoleWriter,
+ ComponentWriter componentWriter) {
+ this.identifiableWriter = identifiableWriter;
+ this.conceptRoleWriter = conceptRoleWriter;
+ this.componentWriter = componentWriter;
+ }
+
+ public void write(JsonGenerator jsonGenerator, AttributeDescriptor attributeDescriptor) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.ATTRIBUTE_LIST);
+ if (attributeDescriptor == null || attributeDescriptor.getComponents().isEmpty()) {
+ jsonGenerator.writeNull();
+ return;
+ }
+
+ AttributeDescriptorImpl attributeDescriptorImpl = (AttributeDescriptorImpl) attributeDescriptor;
+ attributeDescriptorImpl.setId(StructureUtils.ATTRIBUTE_DESCRIPTOR_ID);
+
+ jsonGenerator.writeStartObject();
+ identifiableWriter.write(jsonGenerator, attributeDescriptorImpl);
+
+ writeDataAttributes(jsonGenerator, attributeDescriptorImpl.getComponents());
+
+ jsonGenerator.writeEndObject();
+ }
+
+ private void writeDataAttributes(JsonGenerator jsonGenerator, List attributeDescriptorComponents) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.ATTRIBUTES);
+ jsonGenerator.writeStartArray();
+ if (attributeDescriptorComponents != null && !attributeDescriptorComponents.isEmpty()) {
+ for (DataAttribute dataAttribute : attributeDescriptorComponents) {
+ if (dataAttribute != null) {
+ jsonGenerator.writeStartObject();
+ componentWriter.write(jsonGenerator, dataAttribute);
+ writeAssignmentStatus(jsonGenerator, dataAttribute.isMandatory());
+ conceptRoleWriter.write(jsonGenerator, dataAttribute.getConceptRoles());
+ writeAttributeRelationship(jsonGenerator, dataAttribute.getAttributeRelationship());
+ jsonGenerator.writeEndObject();
+ }
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+
+ private void writeAssignmentStatus(JsonGenerator jg, boolean mandatory) {
+ writeStringField(jg, "assignmentStatus", mandatory ? "Mandatory" : "Conditional");
+ }
+
+ private void writeAttributeRelationship(JsonGenerator jg, AttributeRelationship attributeRelationship) throws IOException {
+ jg.writeFieldName(StructureUtils.ATTRIBUTE_RELATIONSHIP);
+ jg.writeStartObject();
+ if (attributeRelationship instanceof DimensionRelationship) {
+ DimensionRelationship dimensionRelationship = (DimensionRelationship) attributeRelationship;
+ jg.writeFieldName(StructureUtils.DIMENSIONS);
+ List dimensions = dimensionRelationship.getDimensions();
+ jg.writeStartArray();
+ for (String dimension : dimensions) {
+ jg.writeString(dimension);
+ }
+ jg.writeEndArray();
+
+ List groupKeys = dimensionRelationship.getGroupKeys();
+ if (CollectionUtils.isNotEmpty(groupKeys)) {
+ jg.writeFieldName(StructureUtils.ATTACHMENT_GROUPS);
+ jg.writeStartArray();
+ for (String groupKey : groupKeys) {
+ jg.writeString(groupKey);
+ }
+ jg.writeEndArray();
+ }
+
+ } else if (attributeRelationship instanceof ObservationRelationship) {
+ writeStringField(jg, "primaryMeasure", "OBS_VALUE");
+ } else if (attributeRelationship instanceof GroupRelationship) {
+ GroupRelationship groupRelationship = (GroupRelationship) attributeRelationship;
+ jg.writeStringField(StructureUtils.GROUP, groupRelationship.getGroupKey());
+ } else {
+ jg.writeFieldName(StructureUtils.NONE);
+ jg.writeStartObject();
+ jg.writeEndObject();
+ }
+ jg.writeEndObject();
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/BaseWriterJson.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/BaseWriterJson.java
new file mode 100644
index 0000000..80d0a47
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/BaseWriterJson.java
@@ -0,0 +1,20 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.OutputStream;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.SneakyThrows;
+
+public class BaseWriterJson {
+
+ protected final JsonGenerator jsonGenerator;
+
+ @SneakyThrows
+ protected BaseWriterJson(ObjectMapper mapper,
+ OutputStream outputStream) {
+ this.jsonGenerator = mapper.getFactory().createGenerator(outputStream, JsonEncoding.UTF8);
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CategorisationWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CategorisationWriter.java
new file mode 100644
index 0000000..b12256a
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CategorisationWriter.java
@@ -0,0 +1,53 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Categorisation;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+
+public class CategorisationWriter extends MaintainableWriter {
+
+ private final ReferenceResolver referenceResolver;
+ private final ReferenceAdapter referenceAdapter;
+
+ public CategorisationWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ ReferenceResolver referenceResolver,
+ ReferenceAdapter referenceAdapter) {
+ super(versionableWriter, linksWriter);
+ this.referenceResolver = referenceResolver;
+ this.referenceAdapter = referenceAdapter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator jsonGenerator, Categorisation categorisation) throws IOException {
+ super.writeFields(jsonGenerator, categorisation);
+ writeArtefactReference(jsonGenerator, categorisation.getCategorizedArtefact(), StructureUtils.SOURCE);
+ writeArtefactReference(jsonGenerator, categorisation.getCategorizedBy(), StructureUtils.TARGET);
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getCategorisations();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.CATEGORISATIONS;
+ }
+
+ private void writeArtefactReference(JsonGenerator jsonGenerator, ArtefactReference structure, String fieldName)
+ throws IOException {
+ if (structure != null) {
+ final ArtefactReference resolved = referenceResolver.resolve(structure);
+ jsonGenerator.writeStringField(fieldName, referenceAdapter.toAdaptedUrn(resolved));
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CategorySchemeWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CategorySchemeWriter.java
new file mode 100644
index 0000000..f4c5eec
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CategorySchemeWriter.java
@@ -0,0 +1,56 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Category;
+import com.epam.jsdmx.infomodel.sdmx30.CategoryScheme;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class CategorySchemeWriter extends MaintainableWriter {
+
+ private final NameableWriter nameableWriter;
+
+ public CategorySchemeWriter(VersionableWriter versionableWriter, LinksWriter linksWriter, NameableWriter nameableWriter) {
+ super(versionableWriter, linksWriter);
+ this.nameableWriter = nameableWriter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator jsonGenerator, CategoryScheme categoryScheme) throws IOException {
+ super.writeFields(jsonGenerator, categoryScheme);
+ jsonGenerator.writeBooleanField(StructureUtils.IS_PARTIAL, categoryScheme.isPartial());
+ writeCategoriesList(jsonGenerator, categoryScheme.getItems());
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getCategorySchemes();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.CATEGORY_SCHEMES;
+ }
+
+ private void writeCategoriesList(JsonGenerator jsonGenerator, List extends Category> categoryScheme) throws IOException {
+ if (categoryScheme != null) {
+ jsonGenerator.writeFieldName(StructureUtils.CATEGORIES);
+ jsonGenerator.writeStartArray();
+ if (!categoryScheme.isEmpty()) {
+ for (Category category : categoryScheme) {
+ jsonGenerator.writeStartObject();
+ nameableWriter.write(jsonGenerator, category);
+ if (!category.getHierarchy().isEmpty()) {
+ writeCategoriesList(jsonGenerator, category.getHierarchy());
+ }
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodeWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodeWriter.java
new file mode 100644
index 0000000..9a5e8e8
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodeWriter.java
@@ -0,0 +1,43 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.Code;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public interface CodeWriter {
+
+ default void writeCodes(JsonGenerator jsonGenerator,
+ List extends T> codes,
+ String name,
+ NameableWriter nameableWriter) throws IOException {
+ if (CollectionUtils.isNotEmpty(codes)) {
+ jsonGenerator.writeFieldName(name);
+ jsonGenerator.writeStartArray();
+ for (T code : codes) {
+ write(jsonGenerator, code, nameableWriter);
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+
+ default void write(JsonGenerator jsonGenerator,
+ T code,
+ NameableWriter nameableWriter) throws IOException {
+ jsonGenerator.writeStartObject();
+ writeFields(jsonGenerator, code, nameableWriter);
+ jsonGenerator.writeEndObject();
+ }
+
+ default void writeFields(JsonGenerator jsonGenerator,
+ T code,
+ NameableWriter nameableWriter) throws IOException {
+ nameableWriter.write(jsonGenerator, code);
+ if (code.getParentId() != null) {
+ jsonGenerator.writeStringField(StructureUtils.PARENT, code.getParentId());
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodeWriterImpl.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodeWriterImpl.java
new file mode 100644
index 0000000..e8496ae
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodeWriterImpl.java
@@ -0,0 +1,6 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import com.epam.jsdmx.infomodel.sdmx30.Code;
+
+public class CodeWriterImpl implements CodeWriter {
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodelistWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodelistWriter.java
new file mode 100644
index 0000000..e418068
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CodelistWriter.java
@@ -0,0 +1,50 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Codelist;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClass;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClassImpl;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class CodelistWriter extends MaintainableWriter {
+
+ private final CodeWriterImpl codeWriter;
+ private final NameableWriter nameableWriter;
+
+ public CodelistWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ CodeWriterImpl codeWriter,
+ NameableWriter nameableWriter) {
+ super(versionableWriter, linksWriter);
+ this.codeWriter = codeWriter;
+ this.nameableWriter = nameableWriter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator jsonGenerator, Codelist codelist) throws IOException {
+ super.writeFields(jsonGenerator, codelist);
+ jsonGenerator.writeBooleanField(StructureUtils.IS_PARTIAL, codelist.isPartial());
+ codeWriter.writeCodes(jsonGenerator, codelist.getItems(), StructureUtils.CODES, nameableWriter);
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getCodelists();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.CODE_LISTS;
+ }
+
+ @Override
+ public Optional getWritableArtefactStructureClass() {
+ return Optional.of(StructureClassImpl.CODELIST);
+ }
+
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ComponentWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ComponentWriter.java
new file mode 100644
index 0000000..bfe54c9
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ComponentWriter.java
@@ -0,0 +1,37 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.Component;
+import com.epam.jsdmx.infomodel.sdmx30.Representation;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class ComponentWriter {
+
+ private final IdentifiableWriter identifiableWriter;
+ private final RepresentationWriter representationWriter;
+ private final ReferenceResolver referenceResolver;
+
+ public ComponentWriter(IdentifiableWriter identifiableWriter,
+ RepresentationWriter representationWriter,
+ ReferenceResolver referenceResolver) {
+ this.identifiableWriter = identifiableWriter;
+ this.representationWriter = representationWriter;
+ this.referenceResolver = referenceResolver;
+ }
+
+ public void write(JsonGenerator jsonGenerator, Component component) throws IOException {
+ identifiableWriter.write(jsonGenerator, component);
+ if (component.getConceptIdentity() != null) {
+ final ArtefactReference ref = component.getConceptIdentity();
+ jsonGenerator.writeStringField(StructureUtils.CONCEPT_IDENTITY, referenceResolver.resolve(ref).getUrn());
+ }
+ final Representation localRepresentation = component.getLocalRepresentation();
+ if (localRepresentation != null) {
+ representationWriter.writeRepresentation(jsonGenerator, localRepresentation, StructureUtils.LOCAL_REPRESENTATION);
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ConceptRoleWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ConceptRoleWriter.java
new file mode 100644
index 0000000..7610394
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ConceptRoleWriter.java
@@ -0,0 +1,27 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class ConceptRoleWriter {
+
+ private final ReferenceResolver referenceResolver;
+
+ public void write(JsonGenerator jsonGenerator, List conceptRoles) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.CONCEPT_ROLES);
+ jsonGenerator.writeStartArray();
+ if (conceptRoles != null && !conceptRoles.isEmpty()) {
+ for (ArtefactReference conceptRef : conceptRoles) {
+ jsonGenerator.writeString(referenceResolver.resolve(conceptRef).getUrn());
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ConceptSchemeWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ConceptSchemeWriter.java
new file mode 100644
index 0000000..cce56ad
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ConceptSchemeWriter.java
@@ -0,0 +1,59 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Concept;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptScheme;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class ConceptSchemeWriter extends MaintainableWriter {
+
+ private final NameableWriter nameableWriter;
+ private final RepresentationWriter representationWriter;
+
+ public ConceptSchemeWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ NameableWriter nameableWriter,
+ RepresentationWriter representationWriter) {
+ super(versionableWriter, linksWriter);
+ this.nameableWriter = nameableWriter;
+ this.representationWriter = representationWriter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator jsonGenerator, ConceptScheme conceptScheme) throws IOException {
+ super.writeFields(jsonGenerator, conceptScheme);
+ List extends Concept> conceptSchemeItems = conceptScheme.getItems();
+ if (conceptSchemeItems != null) {
+ jsonGenerator.writeFieldName(StructureUtils.CONCEPTS);
+ jsonGenerator.writeStartArray();
+ if (!conceptSchemeItems.isEmpty()) {
+ for (Concept concept : conceptSchemeItems) {
+ jsonGenerator.writeStartObject();
+ nameableWriter.write(jsonGenerator, concept);
+
+ representationWriter.writeRepresentation(jsonGenerator, concept.getCoreRepresentation(), StructureUtils.CORE_REPRESENTATION);
+
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+ jsonGenerator.writeBooleanField(StructureUtils.IS_PARTIAL, conceptScheme.isPartial());
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getConceptSchemes();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.CONCEPT_SCHEMES;
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ContactsWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ContactsWriter.java
new file mode 100644
index 0000000..fd01a1d
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ContactsWriter.java
@@ -0,0 +1,76 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.Contact;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalString;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public class ContactsWriter {
+
+ public void writeContacts(JsonGenerator jsonGenerator, List contacts) throws IOException {
+ if (CollectionUtils.isNotEmpty(contacts)) {
+ jsonGenerator.writeFieldName(StructureUtils.CONTACTS);
+ jsonGenerator.writeStartArray();
+ for (Contact contact : contacts) {
+ if (contact != null) {
+ jsonGenerator.writeStartObject();
+
+ String name = contact.getName();
+ if (name != null) {
+ jsonGenerator.writeStringField(StructureUtils.NAME, name);
+ }
+
+ String organizationUnit = contact.getOrganizationUnit();
+ writeOrganisationUnit(jsonGenerator, organizationUnit);
+
+ InternationalString responsibility = contact.getResponsibility();
+ StructureUtils.writeInternationalString(jsonGenerator, responsibility, StructureUtils.ROLE);
+
+ String telephone = contact.getTelephone();
+ writeOneStringArray(jsonGenerator, telephone, StructureUtils.TELEPHONES);
+
+ String fax = contact.getFax();
+ writeOneStringArray(jsonGenerator, fax, StructureUtils.FAXES);
+
+ String x400 = contact.getX400();
+ writeOneStringArray(jsonGenerator, x400, StructureUtils.X_400_S);
+
+ URI uri = contact.getUri();
+ writeURI(jsonGenerator, uri);
+
+ String email = contact.getEmail();
+ writeOneStringArray(jsonGenerator, email, StructureUtils.EMAILS);
+
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+
+ private void writeOrganisationUnit(JsonGenerator jsonGenerator, String organizationUnit) throws IOException {
+ if (organizationUnit != null) {
+ jsonGenerator.writeStringField(StructureUtils.DEPARTMENT, organizationUnit);
+ }
+ }
+
+ private void writeURI(JsonGenerator jsonGenerator, URI uri) throws IOException {
+ if (uri != null) {
+ writeOneStringArray(jsonGenerator, uri.toString(), StructureUtils.URIS);
+ }
+ }
+
+ private void writeOneStringArray(JsonGenerator jsonGenerator, String parameter, String parameterName) throws IOException {
+ jsonGenerator.writeFieldName(parameterName);
+ jsonGenerator.writeStartArray();
+ if (parameter != null) {
+ jsonGenerator.writeString(parameter);
+ }
+ jsonGenerator.writeEndArray();
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ContentConstraintWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ContentConstraintWriter.java
new file mode 100644
index 0000000..d2ff36a
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/ContentConstraintWriter.java
@@ -0,0 +1,136 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static com.epam.jsdmx.json10.structure.writer.StructureUtils.CONSTRAINT_ROLE_STRING;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Constraint;
+import com.epam.jsdmx.infomodel.sdmx30.DataConstraint;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataConstraint;
+import com.epam.jsdmx.infomodel.sdmx30.ReleaseCalendar;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClassImpl;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public class ContentConstraintWriter extends MaintainableWriter {
+
+ private final ReferenceAdapter referenceAdapter;
+ private final DataKeySetsWriter dataKeySetsWriter;
+ private final CubeRegionWriter cubeRegionWriter;
+ private final MetadataConstraintWriter metadataConstraintWriter;
+
+ public ContentConstraintWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ ReferenceAdapter referenceAdapter,
+ DataKeySetsWriter dataKeySetsWriter,
+ CubeRegionWriter cubeRegionWriter,
+ MetadataConstraintWriter metadataConstraintWriter) {
+ super(versionableWriter, linksWriter);
+ this.referenceAdapter = referenceAdapter;
+ this.dataKeySetsWriter = dataKeySetsWriter;
+ this.cubeRegionWriter = cubeRegionWriter;
+ this.metadataConstraintWriter = metadataConstraintWriter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator gen, Constraint constraint) throws IOException {
+ super.writeFields(gen, constraint);
+ writeConstrainedArtefacts(gen, constraint.getConstrainedArtefacts());
+ if (constraint.getConstraintRoleType() != null) {
+ String constraintRoleType = CONSTRAINT_ROLE_STRING.get(constraint.getConstraintRoleType());
+ gen.writeStringField(StructureUtils.ROLE, constraintRoleType);
+ }
+
+ if (constraint instanceof DataConstraint) {
+ DataConstraint dataConstraint = (DataConstraint) constraint;
+ writeReleaseCalendar(gen, dataConstraint.getReleaseCalendar());
+ if (CollectionUtils.isNotEmpty(dataConstraint.getCubeRegions())) {
+ cubeRegionWriter.write(gen, dataConstraint.getCubeRegions());
+ }
+ if (CollectionUtils.isNotEmpty(dataConstraint.getDataContentKeys())) {
+ dataKeySetsWriter.write(gen, dataConstraint.getDataContentKeys());
+ }
+ } else {
+ MetadataConstraint metadataConstraint = (MetadataConstraint) constraint;
+ writeReleaseCalendar(gen, metadataConstraint.getReleaseCalendar());
+ metadataConstraintWriter.writeMetadataTargetRegions(gen, metadataConstraint.getMetadataTargetRegions());
+ }
+ }
+
+ private void writeConstrainedArtefacts(JsonGenerator gen, List constrainedArtefacts) throws IOException {
+ if (CollectionUtils.isNotEmpty(constrainedArtefacts)) {
+ gen.writeFieldName(StructureUtils.CONSTRAINT_ATTACHMENT);
+ gen.writeStartObject();
+ List providerRef = StructureUtils.getArtefactReferencesByStructureClass(constrainedArtefacts, StructureClassImpl.DATA_PROVIDER);
+ if (providerRef.size() != 0) {
+ gen.writeStringField(StructureUtils.DATA_PROVIDER, referenceAdapter.toAdaptedUrn(providerRef.get(0)));
+ }
+
+ List dataStructures = StructureUtils.getArtefactReferencesByStructureClass(
+ constrainedArtefacts,
+ StructureClassImpl.DATA_STRUCTURE
+ );
+ StructureUtils.writeArtefactReferences(gen, dataStructures, StructureUtils.DATA_STRUCTURES, referenceAdapter);
+ List dataFlows = StructureUtils.getArtefactReferencesByStructureClass(constrainedArtefacts, StructureClassImpl.DATAFLOW);
+ StructureUtils.writeArtefactReferences(gen, dataFlows, StructureUtils.DATAFLOWS, referenceAdapter);
+
+ List metadataStructures = StructureUtils.getArtefactReferencesByStructureClass(
+ constrainedArtefacts,
+ StructureClassImpl.METADATA_STRUCTURE
+ );
+ StructureUtils.writeArtefactReferences(gen, metadataStructures, StructureUtils.METADATA_STRUCTURES, referenceAdapter);
+ List metadataFlows = StructureUtils.getArtefactReferencesByStructureClass(constrainedArtefacts, StructureClassImpl.METADATAFLOW);
+ StructureUtils.writeArtefactReferences(gen, metadataFlows, StructureUtils.METADATAFLOWS, referenceAdapter);
+ List metadataSets = StructureUtils.getArtefactReferencesByStructureClass(constrainedArtefacts, StructureClassImpl.METADATA_SET);
+ StructureUtils.writeArtefactReferences(gen, metadataSets, StructureUtils.METADATA_SETS, referenceAdapter);
+
+ List provisionAgreements = StructureUtils.getArtefactReferencesByStructureClass(
+ constrainedArtefacts,
+ StructureClassImpl.PROVISION_AGREEMENT
+ );
+ StructureUtils.writeArtefactReferences(gen, provisionAgreements, StructureUtils.PROVISION_AGREEMENTS, referenceAdapter);
+ gen.writeEndObject();
+ }
+ }
+
+ private void writeReleaseCalendar(JsonGenerator gen, ReleaseCalendar releaseCalendar) throws IOException {
+ if (releaseCalendar != null) {
+ gen.writeFieldName(StructureUtils.RELEASE_CALENDAR);
+ gen.writeStartObject();
+ String tolerance = releaseCalendar.getTolerance();
+ if (tolerance != null) {
+ gen.writeStringField(StructureUtils.TOLERANCE, tolerance);
+ }
+ String offset = releaseCalendar.getOffset();
+ if (offset != null) {
+ gen.writeStringField(StructureUtils.OFFSET, offset);
+ }
+ String periodicity = releaseCalendar.getPeriodicity();
+ if (periodicity != null) {
+ gen.writeStringField(StructureUtils.PERIODICITY, periodicity);
+ }
+ gen.writeEndObject();
+ }
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ var constraints = new HashSet();
+ constraints.addAll(artefacts.getDataConstraints());
+ constraints.addAll(artefacts.getMetadataConstraints());
+ return constraints;
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.CONTENT_CONSTRAINT;
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CubeRegionWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CubeRegionWriter.java
new file mode 100644
index 0000000..4f316ea
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/CubeRegionWriter.java
@@ -0,0 +1,69 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegion;
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegionKey;
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelection;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public class CubeRegionWriter {
+ private final SelectionValueWriter selectionValueWriter;
+ private final AnnotableWriter annotableWriter;
+
+ public CubeRegionWriter(SelectionValueWriter selectionValueWriter, AnnotableWriter annotableWriter) {
+ this.selectionValueWriter = selectionValueWriter;
+ this.annotableWriter = annotableWriter;
+ }
+
+ public void write(JsonGenerator generator, List cubeRegions) throws IOException {
+ generator.writeFieldName(StructureUtils.CUBE_REGIONS);
+ generator.writeStartArray();
+ if (CollectionUtils.isNotEmpty(cubeRegions)) {
+ for (CubeRegion cubeRegion : cubeRegions) {
+ generator.writeStartObject();
+ generator.writeBooleanField(StructureUtils.IS_INCLUDED, cubeRegion.isIncluded());
+ writeAttributes(generator, cubeRegion.getMemberSelections());
+ writeKeyValues(generator, cubeRegion.getCubeRegionKeys());
+ annotableWriter.write(generator, cubeRegion);
+ generator.writeEndObject();
+ }
+ }
+ generator.writeEndArray();
+ }
+
+ private void writeAttributes(JsonGenerator generator, List memberSelections) throws IOException {
+ if (CollectionUtils.isNotEmpty(memberSelections)) {
+ generator.writeFieldName(StructureUtils.ATTRIBUTES);
+ generator.writeStartArray();
+ for (MemberSelection memberSelection : memberSelections) {
+ if (memberSelection != null) {
+ generator.writeStartObject();
+
+ generator.writeStringField(StructureUtils.ID, memberSelection.getComponentId());
+ selectionValueWriter.writeSelectionValues(generator, memberSelection.getSelectionValues());
+
+ generator.writeEndObject();
+ }
+ }
+ generator.writeEndArray();
+ }
+ }
+
+ private void writeKeyValues(JsonGenerator jsonGenerator, List cubeRegionKeys) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.KEY_VALUES);
+ jsonGenerator.writeStartArray();
+ if (CollectionUtils.isNotEmpty(cubeRegionKeys)) {
+ for (CubeRegionKey cubeRegionKey : cubeRegionKeys) {
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField(StructureUtils.ID, cubeRegionKey.getComponentId());
+ selectionValueWriter.writeSelectionValues(jsonGenerator, cubeRegionKey.getSelectionValues());
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataKeySetsWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataKeySetsWriter.java
new file mode 100644
index 0000000..473442a
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataKeySetsWriter.java
@@ -0,0 +1,67 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.ComponentValue;
+import com.epam.jsdmx.infomodel.sdmx30.DataKey;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeySet;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public class DataKeySetsWriter {
+
+ public void write(JsonGenerator generator, List dataKeySets) throws IOException {
+ if (CollectionUtils.isNotEmpty(dataKeySets)) {
+ generator.writeFieldName(StructureUtils.DATA_KEY_SETS);
+ generator.writeStartArray();
+ for (DataKeySet dataKeySet : dataKeySets) {
+ if (dataKeySet != null) {
+ generator.writeStartObject();
+
+ generator.writeBooleanField(StructureUtils.IS_INCLUDED, dataKeySet.isIncluded());
+
+ writeDataKeys(generator, dataKeySet.getKeys());
+
+ generator.writeEndObject();
+ }
+ }
+ generator.writeEndArray();
+ }
+ }
+
+
+ private void writeDataKeys(JsonGenerator jsonGenerator, List keys) throws IOException {
+ if (CollectionUtils.isNotEmpty(keys)) {
+ jsonGenerator.writeFieldName(StructureUtils.KEYS);
+ jsonGenerator.writeStartArray();
+ for (DataKey dataKey : keys) {
+ if (dataKey != null) {
+ jsonGenerator.writeStartObject();
+ writeKeyValues(jsonGenerator, dataKey.getKeyValues());
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+
+ private void writeKeyValues(JsonGenerator jsonGenerator, List keyValues) throws IOException {
+ if (CollectionUtils.isNotEmpty(keyValues)) {
+ jsonGenerator.writeFieldName(StructureUtils.KEY_VALUES);
+ jsonGenerator.writeStartArray();
+ for (ComponentValue componentValue : keyValues) {
+ if (componentValue != null) {
+ jsonGenerator.writeStartObject();
+
+ jsonGenerator.writeStringField(StructureUtils.ID, componentValue.getComponentId());
+ jsonGenerator.writeStringField(StructureUtils.VALUE, componentValue.getValue());
+
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataStructureComponentsUtils.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataStructureComponentsUtils.java
new file mode 100644
index 0000000..7740907
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataStructureComponentsUtils.java
@@ -0,0 +1,34 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import com.epam.jsdmx.infomodel.sdmx30.Component;
+import com.epam.jsdmx.infomodel.sdmx30.ComponentList;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public final class DataStructureComponentsUtils {
+
+ public static boolean isEmpty(ComponentList componentList) {
+ if (componentList == null) {
+ return true;
+ }
+
+ List components = Optional.of(componentList)
+ .map(ComponentList::getComponents)
+ .orElse(Collections.emptyList());
+ return components.isEmpty();
+ }
+
+ public static boolean areAllEmpty(ComponentList>... componentLists) {
+ for (ComponentList> componentList : componentLists) {
+ if (!isEmpty(componentList)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataStructureDefinitionWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataStructureDefinitionWriter.java
new file mode 100644
index 0000000..0140cf4
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataStructureDefinitionWriter.java
@@ -0,0 +1,86 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.AttributeDescriptor;
+import com.epam.jsdmx.infomodel.sdmx30.DataStructureDefinition;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionDescriptor;
+import com.epam.jsdmx.infomodel.sdmx30.GroupDimensionDescriptor;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureDescriptor;
+import com.epam.jsdmx.serializer.common.TimeDimensionLocalRepresentationAdapter;
+import com.epam.jsdmx.serializer.sdmx21.DataStructure30To21ComponentAdapter;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+
+public class DataStructureDefinitionWriter extends MaintainableWriter {
+
+ private final AttributeListWriter attributeListWriterJson;
+ private final DimensionListWriter dimensionListWriterJson;
+ private final GroupDimensionListWriter groupDimensionListWriter;
+ private final MeasureListWriter measureListWriterJson;
+ private final DataStructure30To21ComponentAdapter dsdComponentAdapter;
+ private final TimeDimensionLocalRepresentationAdapter timeDimensionLocalRepresentationAdapter;
+
+
+ public DataStructureDefinitionWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ AttributeListWriter attributeListWriterJson,
+ DimensionListWriter dimensionListWriterJson,
+ MeasureListWriter measureListWriterJson,
+ GroupDimensionListWriter groupDimensionListWriter,
+ DataStructure30To21ComponentAdapter dsdComponentAdapter,
+ TimeDimensionLocalRepresentationAdapter timeDimensionLocalRepresentationAdapter) {
+ super(versionableWriter, linksWriter);
+ this.attributeListWriterJson = attributeListWriterJson;
+ this.dimensionListWriterJson = dimensionListWriterJson;
+ this.measureListWriterJson = measureListWriterJson;
+ this.groupDimensionListWriter = groupDimensionListWriter;
+ this.dsdComponentAdapter = dsdComponentAdapter;
+ this.timeDimensionLocalRepresentationAdapter = timeDimensionLocalRepresentationAdapter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator jsonGenerator, DataStructureDefinition dsd) throws IOException {
+ super.writeFields(jsonGenerator, dsd);
+ writeDataStructureComponents(jsonGenerator, dsd);
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getDataStructures();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.DATA_STRUCTURES;
+ }
+
+ private void writeDataStructureComponents(JsonGenerator jsonGenerator, DataStructureDefinition dsd) throws IOException {
+ dsd = timeDimensionLocalRepresentationAdapter.adapt(dsd);
+ dsd = dsdComponentAdapter.recompose(dsd);
+ jsonGenerator.writeFieldName(StructureUtils.DATA_STRUCTURE_COMPONENTS);
+ AttributeDescriptor attributeDescriptor = dsd.getAttributeDescriptor();
+ DimensionDescriptor dimensionDescriptor = dsd.getDimensionDescriptor();
+ MeasureDescriptor measureDescriptor = dsd.getMeasureDescriptor();
+ List groupDimensionDescriptors = dsd.getGroupDimensionDescriptors();
+ if (DataStructureComponentsUtils.areAllEmpty(
+ attributeDescriptor,
+ dimensionDescriptor,
+ measureDescriptor
+ )) {
+ jsonGenerator.writeNull();
+ } else {
+ jsonGenerator.writeStartObject();
+ measureListWriterJson.write(jsonGenerator, measureDescriptor);
+ dimensionListWriterJson.write(jsonGenerator, dimensionDescriptor);
+ groupDimensionListWriter.write(jsonGenerator, groupDimensionDescriptors);
+ attributeListWriterJson.write(jsonGenerator, attributeDescriptor);
+ jsonGenerator.writeEndObject();
+ }
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataflowWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataflowWriter.java
new file mode 100644
index 0000000..a2f5fc4
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DataflowWriter.java
@@ -0,0 +1,41 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Dataflow;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+
+public class DataflowWriter extends MaintainableWriter {
+
+ private final ReferenceResolver referenceResolver;
+
+ public DataflowWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ ReferenceResolver referenceResolver) {
+ super(versionableWriter, linksWriter);
+ this.referenceResolver = referenceResolver;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator jsonGenerator, Dataflow dataflow) throws IOException {
+ super.writeFields(jsonGenerator, dataflow);
+ if (dataflow.getStructure() != null) {
+ jsonGenerator.writeStringField(StructureUtils.STRUCTURE, referenceResolver.resolve(dataflow.getStructure()).getUrn());
+ }
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getDataflows();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.DATAFLOWS;
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DefaultHeaderProvider.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DefaultHeaderProvider.java
new file mode 100644
index 0000000..8046754
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DefaultHeaderProvider.java
@@ -0,0 +1,44 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.net.URI;
+import java.time.Instant;
+import java.util.List;
+import java.util.Locale;
+
+import com.epam.jsdmx.infomodel.sdmx30.DefaultLocaleHolder;
+import com.epam.jsdmx.infomodel.sdmx30.Party;
+import com.epam.jsdmx.serializer.sdmx30.common.Header;
+
+import lombok.SneakyThrows;
+import org.apache.commons.lang3.RandomUtils;
+
+public class DefaultHeaderProvider {
+ public static final String SCHEMA
+ = "https://github.com/sdmx-twg/sdmx-json/blob/v1.0/structure-message/tools/schemas/1.0/sdmx-json-structure-schema.json";
+
+ @SneakyThrows
+ public Header provide() {
+ Header header = new Header();
+ header.setSchema(new URI(SCHEMA));
+ header.setId(generateId());
+ header.setContentLanguages(getDefaultLocales());
+ header.setTest(false);
+ header.setPrepared(Instant.now());
+ header.setSender(generateUnknownParty());
+ return header;
+ }
+
+ private String generateId() {
+ return "IDREF" + RandomUtils.nextInt(1000, 9999);
+ }
+
+ private Party generateUnknownParty() {
+ return Party.builder()
+ .id("unknown")
+ .build();
+ }
+
+ private List getDefaultLocales() {
+ return List.of(DefaultLocaleHolder.INSTANCE.get());
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DimensionListWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DimensionListWriter.java
new file mode 100644
index 0000000..44a8af8
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/DimensionListWriter.java
@@ -0,0 +1,95 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import com.epam.jsdmx.infomodel.sdmx30.Dimension;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionComponent;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionDescriptor;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.TimeDimension;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = false)
+public class DimensionListWriter {
+
+ private final IdentifiableWriter identifiableWriter;
+ private final ConceptRoleWriter conceptRoleWriter;
+ private final ComponentWriter componentWriter;
+
+ public DimensionListWriter(IdentifiableWriter identifiableWriter, ConceptRoleWriter conceptRoleWriter, ComponentWriter componentWriter) {
+ this.identifiableWriter = identifiableWriter;
+ this.conceptRoleWriter = conceptRoleWriter;
+ this.componentWriter = componentWriter;
+ }
+
+ public void write(JsonGenerator jsonGenerator, DimensionDescriptor descriptor) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.DIMENSION_LIST);
+
+ if (descriptor == null) {
+ jsonGenerator.writeNull();
+ return;
+ }
+
+ var descriptorImpl = (DimensionDescriptorImpl) descriptor;
+ descriptorImpl.setId(StructureUtils.DIMENSION_DESCRIPTOR_ID);
+
+ jsonGenerator.writeStartObject();
+
+ identifiableWriter.write(jsonGenerator, descriptorImpl);
+ List components = descriptorImpl.getComponents();
+ writeDimensions(jsonGenerator, components);
+ writeTimeDimensions(jsonGenerator, components);
+
+ jsonGenerator.writeEndObject();
+ }
+
+ private void writeDimensions(JsonGenerator jsonGenerator, List components) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.DIMENSIONS);
+ jsonGenerator.writeStartArray();
+ List dimensions = Optional.ofNullable(components)
+ .stream()
+ .flatMap(List::stream)
+ .filter(d -> Dimension.class.isAssignableFrom(d.getClass()))
+ .map(Dimension.class::cast)
+ .collect(Collectors.toList());
+ if (!dimensions.isEmpty()) {
+ for (Dimension dimension : dimensions) {
+ jsonGenerator.writeStartObject();
+ componentWriter.write(jsonGenerator, dimension);
+ jsonGenerator.writeNumberField(StructureUtils.POSITION, dimension.getOrder());
+ jsonGenerator.writeStringField(StructureUtils.TYPE, "Dimension");
+ conceptRoleWriter.write(jsonGenerator, dimension.getConceptRoles());
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+
+ private void writeTimeDimensions(JsonGenerator jsonGenerator, List components) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.TIME_DIMENSIONS);
+ jsonGenerator.writeStartArray();
+ List timeDimensions = Optional.ofNullable(components)
+ .stream()
+ .flatMap(List::stream)
+ .filter(d -> d instanceof TimeDimension)
+ .map(TimeDimension.class::cast)
+ .collect(Collectors.toList());
+ if (!timeDimensions.isEmpty()) {
+ for (TimeDimension dimension : timeDimensions) {
+ jsonGenerator.writeStartObject();
+ componentWriter.write(jsonGenerator, dimension);
+ jsonGenerator.writeNumberField(StructureUtils.POSITION, dimension.getOrder());
+ jsonGenerator.writeStringField(StructureUtils.TYPE, "TimeDimension");
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/GroupDimensionListWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/GroupDimensionListWriter.java
new file mode 100644
index 0000000..50699b1
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/GroupDimensionListWriter.java
@@ -0,0 +1,40 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.GroupDimensionDescriptor;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public class GroupDimensionListWriter {
+
+ private final IdentifiableWriter identifiableWriter;
+
+ public GroupDimensionListWriter(IdentifiableWriter identifiableWriter) {
+ this.identifiableWriter = identifiableWriter;
+ }
+
+ public void write(JsonGenerator jsonGenerator, List descriptors) throws IOException {
+ if (CollectionUtils.isEmpty(descriptors)) {
+ return;
+ }
+
+ jsonGenerator.writeArrayFieldStart(StructureUtils.GROUPS);
+
+ for (GroupDimensionDescriptor descriptor : descriptors) {
+ jsonGenerator.writeStartObject();
+ identifiableWriter.write(jsonGenerator, descriptor);
+
+ jsonGenerator.writeFieldName(StructureUtils.GROUP_DIMENSIONS);
+ String[] dimensionsStringList = descriptor.getDimensions().toArray(new String[0]);
+ jsonGenerator.writeArray(dimensionsStringList, 0, dimensionsStringList.length);
+
+ jsonGenerator.writeEndObject();
+ }
+
+ jsonGenerator.writeEndArray();
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/HierarchicalCodelistWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/HierarchicalCodelistWriter.java
new file mode 100644
index 0000000..e102743
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/HierarchicalCodelistWriter.java
@@ -0,0 +1,128 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.CodingFormat;
+import com.epam.jsdmx.infomodel.sdmx30.Facet;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchicalCode;
+import com.epam.jsdmx.infomodel.sdmx30.Hierarchy;
+import com.epam.jsdmx.infomodel.sdmx30.Level;
+import com.epam.jsdmx.infomodel.sdmx30.TextFormatImpl;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public class HierarchicalCodelistWriter extends MaintainableWriter {
+
+ private final IdentifiableWriter identifiableWriter;
+ private final NameableWriter nameableWriter;
+ private final ReferenceResolver referenceResolver;
+ private final ReferenceAdapter referenceAdapter;
+
+ public HierarchicalCodelistWriter(VersionableWriter versionableWriter,
+ LinksWriter linksWriter,
+ IdentifiableWriter identifiableWriter,
+ NameableWriter nameableWriter,
+ ReferenceResolver referenceResolver,
+ ReferenceAdapter referenceAdapter) {
+ super(versionableWriter, linksWriter);
+ this.identifiableWriter = identifiableWriter;
+ this.nameableWriter = nameableWriter;
+ this.referenceResolver = referenceResolver;
+ this.referenceAdapter = referenceAdapter;
+ }
+
+ @Override
+ protected void writeFields(JsonGenerator gen, Hierarchy hierarchy) throws IOException {
+ super.writeFields(gen, hierarchy);
+ if (CollectionUtils.isNotEmpty(hierarchy.getCodes())) {
+ gen.writeFieldName(StructureUtils.HIERARCHIES);
+ gen.writeStartArray();
+ writeHierarchies(gen, hierarchy.getCodes());
+ gen.writeEndArray();
+ }
+ writeHierarchyLevel(gen, hierarchy.getLevel());
+ }
+
+ private void writeHierarchies(JsonGenerator gen, List codes) throws IOException {
+ // write single hierarchy
+ gen.writeStartObject();
+ gen.writeStringField(StructureUtils.ID, "Hierarchy");
+ gen.writeStringField(StructureUtils.NAME, "Hierarchy");
+ gen.writeFieldName(StructureUtils.HIERARCHICAL_CODES);
+ gen.writeStartArray();
+ writeHierarchicalCodes(gen, codes);
+ gen.writeEndArray();
+ gen.writeEndObject();
+ }
+
+ @Override
+ protected Set extractArtefacts(Artefacts artefacts) {
+ return artefacts.getHierarchies();
+ }
+
+ @Override
+ protected String getArrayName() {
+ return StructureUtils.HIERARCHICAL_CODELISTS;
+ }
+
+ private void writeHierarchyLevel(JsonGenerator gen, Level level) throws IOException {
+ if (level != null) {
+ gen.writeFieldName(StructureUtils.LEVEL);
+ gen.writeStartObject();
+ nameableWriter.write(gen, level);
+ writeCodingFormat(gen, level);
+ writeHierarchyLevel(gen, level.getChild());
+ gen.writeEndObject();
+ }
+
+ }
+
+ private void writeCodingFormat(JsonGenerator gen, Level level) throws IOException {
+ List levelCodeFormat = level.getCodeFormat();
+ if (levelCodeFormat != null) {
+ gen.writeFieldName(StructureUtils.CODING_FORMAT);
+ gen.writeStartObject();
+ for (CodingFormat codingFormat : levelCodeFormat) {
+ if (codingFormat != null) {
+ Facet format = codingFormat.getCodingFormat();
+ TextFormatImpl textFormat = new TextFormatImpl(Set.of(format));
+ StructureUtils.writeCommonFormatAttributes(gen, textFormat);
+ }
+ }
+ gen.writeEndObject();
+ }
+ }
+
+ private void writeHierarchicalCodes(JsonGenerator gen, List codes) throws IOException {
+ if (codes != null) {
+ if (!codes.isEmpty()) {
+ for (HierarchicalCode code : codes) {
+ gen.writeStartObject();
+ identifiableWriter.write(gen, code);
+ gen.writeStringField(StructureUtils.VALID_FROM, StructureUtils.mapInstantToString(code.getValidFrom()));
+ gen.writeStringField(StructureUtils.VALID_TO, StructureUtils.mapInstantToString(code.getValidTo()));
+ if (code.getCode() != null) {
+ final ArtefactReference resolvedCodeRef = referenceResolver.resolve(code.getCode());
+ gen.writeStringField(StructureUtils.CODE, referenceAdapter.toAdaptedUrn(resolvedCodeRef));
+ gen.writeStringField(StructureUtils.CODE_ID, code.getCode().getItemId());
+ }
+ gen.writeStringField(StructureUtils.LEVEL, code.getLevelId());
+ if (code.getHierarchicalCodes() != null && !code.getHierarchicalCodes().isEmpty()) {
+ gen.writeFieldName(StructureUtils.HIERARCHICAL_CODES);
+ gen.writeStartArray();
+ writeHierarchicalCodes(gen, code.getHierarchicalCodes());
+ gen.writeEndArray();
+ }
+ gen.writeEndObject();
+ }
+ }
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/IdentifiableWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/IdentifiableWriter.java
new file mode 100644
index 0000000..37fb622
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/IdentifiableWriter.java
@@ -0,0 +1,26 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefact;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.Data;
+
+@Data
+public class IdentifiableWriter {
+
+ private AnnotableWriter annotableWriter;
+
+ public IdentifiableWriter(AnnotableWriter annotableWriter) {
+ this.annotableWriter = annotableWriter;
+ }
+
+ public void write(JsonGenerator jsonGenerator, T artefact) throws IOException {
+ annotableWriter.write(jsonGenerator, artefact);
+ String id = artefact.getId();
+ if (id != null) {
+ jsonGenerator.writeStringField(StructureUtils.ID, id);
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/Json10StructureWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/Json10StructureWriter.java
new file mode 100644
index 0000000..be8f298
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/Json10StructureWriter.java
@@ -0,0 +1,88 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefact;
+import com.epam.jsdmx.serializer.sdmx30.structure.StructureWriter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class Json10StructureWriter extends BaseWriterJson implements StructureWriter {
+
+ protected final MetaWriter metaWriter;
+ protected final List extends MaintainableWriter extends MaintainableArtefact>> writers;
+
+ public Json10StructureWriter(ObjectMapper mapper,
+ OutputStream outputStream,
+ MetaWriter metaWriter,
+ List extends MaintainableWriter extends MaintainableArtefact>> writers) {
+ super(mapper, outputStream);
+ this.metaWriter = metaWriter;
+ this.writers = writers;
+ }
+
+ @Override
+ public void write(Artefacts artefacts) {
+ try {
+ if (artefacts != null) {
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeFieldName(StructureUtils.DATA);
+
+ jsonGenerator.writeStartObject();
+ writeArtefacts(artefacts);
+ jsonGenerator.writeEndObject();
+
+ metaWriter.writeDefaultHeader(jsonGenerator);
+
+ jsonGenerator.writeEndObject();
+
+ jsonGenerator.flush();
+ jsonGenerator.close();
+ }
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ protected void writeArtefacts(Artefacts artefacts) throws IOException {
+
+ writers.forEach(writer -> {
+ Set extends MaintainableArtefact> maintainableArtefacts = writer.extractArtefacts(artefacts);
+ try {
+ if (!maintainableArtefacts.isEmpty()) {
+ jsonGenerator.writeFieldName(writer.getArrayName());
+ jsonGenerator.writeStartArray();
+ maintainableArtefacts.forEach(art -> {
+ try {
+ if (art.getVersion().isStable()) {
+ writer.write(jsonGenerator, art);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ jsonGenerator.writeEndArray();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ protected void writeArtefact(MaintainableWriter writer,
+ Set extends T> artefacts,
+ String artefactName) throws IOException {
+ if (artefacts != null && !artefacts.isEmpty()) {
+ jsonGenerator.writeFieldName(artefactName);
+ jsonGenerator.writeStartArray();
+ for (T artefact : artefacts) {
+ writer.write(jsonGenerator, artefact);
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/Json10WriterFactory.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/Json10WriterFactory.java
new file mode 100644
index 0000000..fb71635
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/Json10WriterFactory.java
@@ -0,0 +1,136 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefact;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClass;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+import com.epam.jsdmx.serializer.common.TimeDimensionLocalRepresentationAdapter;
+import com.epam.jsdmx.serializer.sdmx21.DataStructure30To21ComponentAdapter;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+import com.epam.jsdmx.serializer.sdmx30.common.DefaultReferenceAdapter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class Json10WriterFactory {
+
+ private final ObjectMapper mapper;
+ private final List languagePriorities;
+ private final DefaultHeaderProvider defaultHeaderProvider;
+ private final DataStructure30To21ComponentAdapter dsdComponentAdapter;
+ private final ReferenceResolver referenceResolver;
+ private final ReferenceAdapter referenceAdapter;
+ private final TimeDimensionLocalRepresentationAdapter timeDimensionLocalRepresentationAdapter;
+
+ public Json10WriterFactory(ObjectMapper mapper,
+ DataStructure30To21ComponentAdapter dsdComponentAdapter,
+ TimeDimensionLocalRepresentationAdapter timeDimensionLocalRepresentationAdapter,
+ ReferenceResolver referenceResolver,
+ List languagePriorities,
+ DefaultHeaderProvider defaultHeaderProvider,
+ ReferenceAdapter referenceAdapter) {
+ this.mapper = mapper;
+ this.languagePriorities = languagePriorities;
+ this.defaultHeaderProvider = defaultHeaderProvider;
+ this.dsdComponentAdapter = dsdComponentAdapter;
+ this.referenceResolver = referenceResolver;
+ this.referenceAdapter = referenceAdapter;
+ this.timeDimensionLocalRepresentationAdapter = timeDimensionLocalRepresentationAdapter;
+ }
+
+ public Json10WriterFactory(ObjectMapper mapper,
+ DataStructure30To21ComponentAdapter dsdComponentAdapter,
+ TimeDimensionLocalRepresentationAdapter timeDimensionLocalRepresentationAdapter,
+ ReferenceResolver referenceResolver,
+ List languagePriorities,
+ DefaultHeaderProvider defaultHeaderProvider) {
+ this(
+ mapper,
+ dsdComponentAdapter,
+ timeDimensionLocalRepresentationAdapter,
+ referenceResolver,
+ languagePriorities,
+ defaultHeaderProvider,
+ new DefaultReferenceAdapter()
+ );
+ }
+
+
+ public Json10StructureWriter newInstance(OutputStream writeTo) {
+ return new Json10StructureWriter(
+ mapper,
+ writeTo,
+ new MetaWriter(languagePriorities, defaultHeaderProvider),
+ writers()
+ );
+ }
+
+ public Json10StructureWriter newInstance(OutputStream writeTo,
+ Map> customStructureWriters) {
+ var defaultStructureWriters = writers();
+ List> structureWriters = new ArrayList<>();
+ for (var writer : defaultStructureWriters) {
+ writer.getWritableArtefactStructureClass()
+ .filter(customStructureWriters::containsKey)
+ .map(customStructureWriters::get)
+ .ifPresentOrElse(structureWriters::add, () -> structureWriters.add(writer));
+ }
+ return new Json10StructureWriter(
+ mapper,
+ writeTo,
+ new MetaWriter(languagePriorities, defaultHeaderProvider),
+ structureWriters
+ );
+ }
+
+ private List extends MaintainableWriter extends MaintainableArtefact>> writers() {
+ final LinksWriter linksWriter = new LinksWriter(referenceAdapter);
+ final AnnotableWriter annotableWriter = new AnnotableWriter(linksWriter);
+ final IdentifiableWriter identifiableWriter = new IdentifiableWriter(annotableWriter);
+ final NameableWriter nameableWriter = new NameableWriter(identifiableWriter);
+ final VersionableWriter versionableWriter = new VersionableWriter(nameableWriter);
+
+ final ContactsWriter contactsWriter = new ContactsWriter();
+ final OrganisationWriter organisationWriter = new OrganisationWriter(contactsWriter, nameableWriter);
+
+ final RepresentationWriter representationWriter = new RepresentationWriter(referenceResolver, referenceAdapter);
+
+ final CodeWriterImpl codeImplWriter = new CodeWriterImpl();
+
+ final ConceptRoleWriter conceptRoleWriter = new ConceptRoleWriter(referenceResolver);
+ final ComponentWriter componentWriter = new ComponentWriter(identifiableWriter, representationWriter, referenceResolver);
+ final AttributeListWriter attributeListWriter = new AttributeListWriter(identifiableWriter, conceptRoleWriter, componentWriter);
+ final DimensionListWriter dimensionListWriter = new DimensionListWriter(identifiableWriter, conceptRoleWriter, componentWriter);
+ final MeasureListWriter measureListWriter = new MeasureListWriter(identifiableWriter, conceptRoleWriter, componentWriter);
+ final GroupDimensionListWriter groupDimensionListWriter = new GroupDimensionListWriter(identifiableWriter);
+ final DataKeySetsWriter dataKeySetsWriter = new DataKeySetsWriter();
+ final CubeRegionWriter cubeRegionWriter = new CubeRegionWriter(new SelectionValueWriter(), annotableWriter);
+ final MetadataConstraintWriter metadataConstraintWriter = new MetadataConstraintWriter(new SelectionValueWriter());
+
+ return List.of(
+ new AgencySchemeWriter(versionableWriter, linksWriter, organisationWriter),
+ new CategorisationWriter(versionableWriter, linksWriter, referenceResolver, referenceAdapter),
+ new CategorySchemeWriter(versionableWriter, linksWriter, nameableWriter),
+ new CodelistWriter(versionableWriter, linksWriter, codeImplWriter, nameableWriter),
+ new ConceptSchemeWriter(versionableWriter, linksWriter, nameableWriter, representationWriter),
+ new DataStructureDefinitionWriter(
+ versionableWriter,
+ linksWriter,
+ attributeListWriter,
+ dimensionListWriter,
+ measureListWriter,
+ groupDimensionListWriter,
+ dsdComponentAdapter,
+ timeDimensionLocalRepresentationAdapter
+ ),
+ new DataflowWriter(versionableWriter, linksWriter, referenceResolver),
+ new HierarchicalCodelistWriter(versionableWriter, linksWriter, identifiableWriter, nameableWriter, referenceResolver, referenceAdapter),
+ new ContentConstraintWriter(versionableWriter, linksWriter, referenceAdapter, dataKeySetsWriter, cubeRegionWriter, metadataConstraintWriter)
+ );
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/JsonRuntimeException.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/JsonRuntimeException.java
new file mode 100644
index 0000000..704f4d8
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/JsonRuntimeException.java
@@ -0,0 +1,8 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+
+public class JsonRuntimeException extends RuntimeException {
+ public JsonRuntimeException(IOException e) {
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/LinksWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/LinksWriter.java
new file mode 100644
index 0000000..28db649
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/LinksWriter.java
@@ -0,0 +1,49 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.InternationalUri;
+import com.epam.jsdmx.infomodel.sdmx30.Link;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class LinksWriter {
+
+ private final ReferenceAdapter referenceAdapter;
+
+ public LinksWriter(ReferenceAdapter referenceAdapter) {
+ this.referenceAdapter = referenceAdapter;
+ }
+
+ public void writeLinks(JsonGenerator jsonGenerator, List links) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.LINKS);
+ jsonGenerator.writeStartArray();
+ if (links != null && !links.isEmpty()) {
+ for (Link link : links) {
+ writeLink(jsonGenerator, link);
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+
+ public void writeLinks(JsonGenerator jsonGenerator, InternationalUri uri) throws IOException {
+ if (uri != null) {
+ jsonGenerator.writeFieldName(StructureUtils.LINKS);
+ jsonGenerator.writeStartArray();
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField(StructureUtils.URI, uri.getForDefaultLocale().toString());
+ jsonGenerator.writeEndObject();
+ jsonGenerator.writeEndArray();
+ }
+ }
+
+ private void writeLink(JsonGenerator jsonGenerator, Link link) throws IOException {
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField(StructureUtils.URN, referenceAdapter.adaptUrn(link.getUrn()));
+ jsonGenerator.writeStringField(StructureUtils.TYPE, referenceAdapter.adaptType(link.getType()));
+ jsonGenerator.writeStringField(StructureUtils.REL, link.getRel());
+ jsonGenerator.writeEndObject();
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MaintainableWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MaintainableWriter.java
new file mode 100644
index 0000000..2682685
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MaintainableWriter.java
@@ -0,0 +1,60 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefact;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClass;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RequiredArgsConstructor
+public abstract class MaintainableWriter {
+
+ private final VersionableWriter versionableWriter;
+ private final LinksWriter linksWriter;
+
+ public void write(JsonGenerator jsonGenerator, MaintainableArtefact artefact) throws IOException {
+ T artefactCasted = (T) artefact;
+ jsonGenerator.writeStartObject();
+ writeFields(jsonGenerator, artefactCasted);
+ jsonGenerator.writeEndObject();
+ }
+
+ protected void writeFields(JsonGenerator jsonGenerator, T artefact) throws IOException {
+ linksWriter.writeLinks(jsonGenerator, artefact.getLinks());
+ versionableWriter.write(jsonGenerator, artefact);
+ jsonGenerator.writeBooleanField(StructureUtils.IS_FINAL, artefact.getVersion().isStable());
+ jsonGenerator.writeStringField(StructureUtils.AGENCY_ID, artefact.getOrganizationId());
+ }
+
+ public void writeAndClose(JsonGenerator jsonGenerator, T maintainableArtefact) {
+ try (jsonGenerator) {
+ write(jsonGenerator, maintainableArtefact);
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ public Optional getWritableArtefactStructureClass() {
+ // if there is a need to find writer by its maintainable artefact type
+ return Optional.empty();
+ }
+
+ protected Set extractArtefacts(Artefacts artefacts) {
+ //different for writers with stream and the ones with dto
+ return Collections.emptySet();
+ }
+
+ protected String getArrayName() {
+ //different for writers with stream and the ones with dto
+ return null;
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MeasureListWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MeasureListWriter.java
new file mode 100644
index 0000000..57d441d
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MeasureListWriter.java
@@ -0,0 +1,59 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.Measure;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureDescriptor;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureDescriptorImpl;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.EqualsAndHashCode;
+import org.apache.commons.collections4.CollectionUtils;
+
+@EqualsAndHashCode(callSuper = false)
+public class MeasureListWriter {
+
+ private final IdentifiableWriter identifiableWriter;
+ private final ConceptRoleWriter conceptRoleWriter;
+ private final ComponentWriter componentWriter;
+
+ public MeasureListWriter(IdentifiableWriter identifiableWriter, ConceptRoleWriter conceptRoleWriter, ComponentWriter componentWriter) {
+ this.identifiableWriter = identifiableWriter;
+ this.conceptRoleWriter = conceptRoleWriter;
+ this.componentWriter = componentWriter;
+ }
+
+ public void write(JsonGenerator jsonGenerator, MeasureDescriptor identifiableArtefact) throws IOException {
+ jsonGenerator.writeFieldName(StructureUtils.MEASURE_LIST);
+
+ if (identifiableArtefact == null) {
+ jsonGenerator.writeNull();
+ return;
+ }
+
+ // set fixed id
+ var measureDescriptor = (MeasureDescriptorImpl) identifiableArtefact;
+ measureDescriptor.setId(StructureUtils.MEASURE_DESCRIPTOR_ID);
+
+ jsonGenerator.writeStartObject();
+
+ identifiableWriter.write(jsonGenerator, measureDescriptor);
+ writeMeasures(jsonGenerator, measureDescriptor.getComponents());
+
+ jsonGenerator.writeEndObject();
+ }
+
+ private void writeMeasures(JsonGenerator jsonGenerator, List components) throws IOException {
+ jsonGenerator.writeFieldName("primaryMeasure");
+
+ if (CollectionUtils.isNotEmpty(components)) {
+ jsonGenerator.writeStartObject();
+
+ final Measure primaryMeasure = components.get(0);
+ componentWriter.write(jsonGenerator, primaryMeasure);
+
+ jsonGenerator.writeEndObject();
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MetaWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MetaWriter.java
new file mode 100644
index 0000000..488948b
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MetaWriter.java
@@ -0,0 +1,66 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Locale.LanguageRange;
+
+import com.epam.jsdmx.infomodel.sdmx30.InternationalString;
+import com.epam.jsdmx.infomodel.sdmx30.Party;
+import com.epam.jsdmx.serializer.sdmx30.common.Header;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.SneakyThrows;
+
+public class MetaWriter {
+
+ private final List languagePriorities;
+ private final DefaultHeaderProvider defaultHeaderProvider;
+
+ public MetaWriter(List languagePriorities, DefaultHeaderProvider defaultHeaderProvider) {
+ this.languagePriorities = languagePriorities;
+ this.defaultHeaderProvider = defaultHeaderProvider;
+ }
+
+ @SneakyThrows
+ public void writeHeader(JsonGenerator jsonGenerator, Header header) {
+ if (header == null) {
+ return;
+ }
+ jsonGenerator.writeFieldName(StructureUtils.META);
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField(StructureUtils.SCHEMA, header.getSchema().toString());
+ jsonGenerator.writeStringField(StructureUtils.ID, header.getId());
+ jsonGenerator.writeBooleanField(StructureUtils.TEST, header.isTest());
+ jsonGenerator.writeStringField(StructureUtils.PREPARED, header.getPrepared().toString());
+ jsonGenerator.writeArrayFieldStart(StructureUtils.CONTENT_LANGUAGES);
+ for (Locale locale : header.getContentLanguages()) {
+ jsonGenerator.writeString(locale.toString());
+ }
+ jsonGenerator.writeEndArray();
+
+ final InternationalString name = header.getName();
+ if (name != null) {
+ jsonGenerator.writeStringField(StructureUtils.NAME, name.getForRanges(languagePriorities));
+ StructureUtils.writeInternationalString(jsonGenerator, name, StructureUtils.NAMES);
+ }
+
+ writeParty(jsonGenerator, header.getSender());
+ jsonGenerator.writeEndObject();
+ }
+
+ public void writeDefaultHeader(JsonGenerator jsonGenerator) {
+ Header header = defaultHeaderProvider.provide();
+ writeHeader(jsonGenerator, header);
+ }
+
+ @SneakyThrows
+ private void writeParty(JsonGenerator jsonGenerator, Party party) {
+ jsonGenerator.writeFieldName(StructureUtils.SENDER);
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField(StructureUtils.ID, party.getId());
+ if (party.getName() != null) {
+ jsonGenerator.writeStringField(StructureUtils.NAME, party.getName().getForDefaultLocale());
+ }
+ jsonGenerator.writeEndObject();
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MetadataConstraintWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MetadataConstraintWriter.java
new file mode 100644
index 0000000..4c54eb8
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/MetadataConstraintWriter.java
@@ -0,0 +1,53 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelection;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataTargetRegion;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+
+public class MetadataConstraintWriter {
+ private final SelectionValueWriter selectionValueWriter;
+
+ public MetadataConstraintWriter(SelectionValueWriter selectionValueWriter) {
+ this.selectionValueWriter = selectionValueWriter;
+ }
+
+ public void writeMetadataTargetRegions(JsonGenerator gen, List metadataTargetRegions) throws IOException {
+ if (CollectionUtils.isNotEmpty(metadataTargetRegions)) {
+ gen.writeFieldName(StructureUtils.METADATA_TARGET_REGIONS);
+ gen.writeStartArray();
+ for (MetadataTargetRegion metadataTargetRegion : metadataTargetRegions) {
+ if (metadataTargetRegion != null) {
+ gen.writeStartObject();
+ gen.writeBooleanField(StructureUtils.INCLUDE, metadataTargetRegion.isIncluded());
+ writeMemberSelections(gen, metadataTargetRegion.getMemberSelections());
+ gen.writeEndObject();
+ }
+ }
+ gen.writeEndArray();
+ }
+ }
+
+ private void writeMemberSelections(JsonGenerator generator, List memberSelections) throws IOException {
+ if (CollectionUtils.isNotEmpty(memberSelections)) {
+ generator.writeFieldName(StructureUtils.ATTRIBUTES);
+ generator.writeStartArray();
+ for (MemberSelection memberSelection : memberSelections) {
+ if (memberSelection != null) {
+ generator.writeStartObject();
+
+ generator.writeStringField(StructureUtils.ID, memberSelection.getComponentId());
+ selectionValueWriter.writeSelectionValues(generator, memberSelection.getSelectionValues());
+
+ generator.writeEndObject();
+ }
+ }
+ generator.writeEndArray();
+ }
+ }
+
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/NameableWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/NameableWriter.java
new file mode 100644
index 0000000..adf23df
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/NameableWriter.java
@@ -0,0 +1,39 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import com.epam.jsdmx.infomodel.sdmx30.NameableArtefact;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class NameableWriter {
+
+ private final IdentifiableWriter identifiableWriter;
+ private List languagePriorities;
+
+ public NameableWriter(IdentifiableWriter identifiableWriter) {
+ this.identifiableWriter = identifiableWriter;
+ }
+
+ public NameableWriter(IdentifiableWriter identifiableWriter, List languagePriorities) {
+ this.identifiableWriter = identifiableWriter;
+ this.languagePriorities = languagePriorities;
+ }
+
+ public void write(JsonGenerator jsonGenerator, NameableArtefact artefact) throws IOException {
+ identifiableWriter.write(jsonGenerator, artefact);
+ if (artefact.getName() != null) {
+ jsonGenerator.writeStringField(StructureUtils.NAME, artefact.getName().getForRanges(languagePriorities));
+ StructureUtils.writeInternationalString(jsonGenerator, artefact.getName(), StructureUtils.NAMES);
+ }
+ if (artefact.getDescription() != null && !artefact.getDescription().isEmpty()) {
+ jsonGenerator.writeStringField(
+ StructureUtils.DESCRIPTION,
+ artefact.getDescription().getForRanges(languagePriorities)
+ );
+ StructureUtils.writeInternationalString(jsonGenerator, artefact.getDescription(), StructureUtils.DESCRIPTIONS);
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/OrganisationWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/OrganisationWriter.java
new file mode 100644
index 0000000..13b7486
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/OrganisationWriter.java
@@ -0,0 +1,57 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.epam.jsdmx.infomodel.sdmx30.Contact;
+import com.epam.jsdmx.infomodel.sdmx30.Organisation;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.AllArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
+
+@AllArgsConstructor
+public class OrganisationWriter {
+
+ private final ContactsWriter contactsWriter;
+ private final NameableWriter nameableWriter;
+
+ > void writeOrganisation(JsonGenerator jsonGenerator, List extends T> organisationItems, String organisationName)
+ throws IOException {
+ if (CollectionUtils.isNotEmpty(organisationItems)) {
+ Map parentIdWithHierarchy = new HashMap<>();
+ for (T organisat : organisationItems) {
+ List extends Organisation>> children = organisat.getHierarchy();
+ if (CollectionUtils.isNotEmpty(children)) {
+ for (Organisation> child : children) {
+ parentIdWithHierarchy.put(child.getId(), organisat.getId());
+ }
+ }
+ }
+
+ jsonGenerator.writeFieldName(organisationName);
+ jsonGenerator.writeStartArray();
+ for (T organisation : organisationItems) {
+ writeOrganisation(jsonGenerator, parentIdWithHierarchy, organisation);
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+
+ private > void writeOrganisation(JsonGenerator jsonGenerator, Map parentIdWithHierarchy, T organisation)
+ throws IOException {
+ if (organisation != null) {
+ jsonGenerator.writeStartObject();
+ nameableWriter.write(jsonGenerator, organisation);
+ List contacts = organisation.getContacts();
+ contactsWriter.writeContacts(jsonGenerator, contacts);
+ String id = organisation.getId();
+ if (parentIdWithHierarchy.containsKey(id)) {
+ jsonGenerator.writeStringField(StructureUtils.PARENT, parentIdWithHierarchy.get(id));
+ }
+ jsonGenerator.writeEndObject();
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/RepresentationWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/RepresentationWriter.java
new file mode 100644
index 0000000..bdc4c2f
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/RepresentationWriter.java
@@ -0,0 +1,56 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Optional;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.Facet;
+import com.epam.jsdmx.infomodel.sdmx30.Representation;
+import com.epam.jsdmx.infomodel.sdmx30.TextFormatImpl;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class RepresentationWriter {
+
+ private final ReferenceResolver referenceResolver;
+ private final ReferenceAdapter referenceAdapter;
+
+ public void writeRepresentation(JsonGenerator jsonGenerator,
+ Representation localRepresentation,
+ String representationName) throws IOException {
+ jsonGenerator.writeFieldName(representationName);
+ jsonGenerator.writeStartObject();
+ if (localRepresentation != null) {
+ if (localRepresentation.isEnumerated()) {
+ final ArtefactReference ref = localRepresentation.enumerated();
+ final ArtefactReference resolvedRef = referenceResolver.resolve(ref);
+ jsonGenerator.writeStringField(StructureUtils.ENUMERATION, referenceAdapter.toAdaptedUrn(resolvedRef));
+ } else {
+ Set facets = localRepresentation.nonEnumerated();
+ TextFormatImpl textFormat = new TextFormatImpl(facets);
+ jsonGenerator.writeFieldName("textFormat");
+ jsonGenerator.writeStartObject();
+
+ writeTextFormat(jsonGenerator, textFormat);
+
+ jsonGenerator.writeEndObject();
+ }
+ }
+ jsonGenerator.writeEndObject();
+ }
+
+ private void writeTextFormat(JsonGenerator jsonGenerator, TextFormatImpl textFormat) throws IOException {
+
+ StructureUtils.writeCommonFormatAttributes(jsonGenerator, textFormat);
+ Optional decimals = textFormat.getDecimals();
+ if (decimals.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.DECIMALS, decimals.get());
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/SelectionValueWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/SelectionValueWriter.java
new file mode 100644
index 0000000..abb37fe
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/SelectionValueWriter.java
@@ -0,0 +1,106 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.epam.jsdmx.infomodel.sdmx30.AfterPeriod;
+import com.epam.jsdmx.infomodel.sdmx30.BeforePeriod;
+import com.epam.jsdmx.infomodel.sdmx30.LocalisedMemberValue;
+import com.epam.jsdmx.infomodel.sdmx30.MemberValue;
+import com.epam.jsdmx.infomodel.sdmx30.RangePeriod;
+import com.epam.jsdmx.infomodel.sdmx30.SelectionValue;
+import com.epam.jsdmx.infomodel.sdmx30.TimeRangePeriod;
+import com.epam.jsdmx.infomodel.sdmx30.TimeRangeValue;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.tuple.Pair;
+
+public class SelectionValueWriter {
+
+ public void writeSelectionValues(JsonGenerator jsonGenerator, List selectionValues) throws IOException {
+ if (CollectionUtils.isNotEmpty(selectionValues)) {
+ List timeRanges = selectionValues.stream().filter(Objects::nonNull)
+ .filter(TimeRangeValue.class::isInstance)
+ .collect(Collectors.toList());
+ writeTimeRanges(jsonGenerator, timeRanges);
+
+ List values = selectionValues.stream().filter(Objects::nonNull)
+ .filter(value -> !(value instanceof TimeRangeValue))
+ .collect(Collectors.toList());
+ writeValues(jsonGenerator, values);
+
+ //TODO cascadeValues in question
+ }
+ }
+
+ private void writeTimeRanges(JsonGenerator jsonGenerator, List timeRanges) throws IOException {
+ if (CollectionUtils.isNotEmpty(timeRanges)) {
+ List> values = new ArrayList<>();
+ for (SelectionValue timeRange : timeRanges) {
+ if (timeRange != null) {
+ if (timeRange instanceof AfterPeriod) {
+ AfterPeriod afterPeriod = (AfterPeriod) timeRange;
+ values.add(Pair.of(afterPeriod, StructureUtils.AFTER_PERIOD));
+ }
+ if (timeRange instanceof BeforePeriod) {
+ BeforePeriod beforePeriod = (BeforePeriod) timeRange;
+ values.add(Pair.of(beforePeriod, StructureUtils.BEFORE_PERIOD));
+ }
+ if (timeRange instanceof RangePeriod) {
+ RangePeriod rangePeriod = (RangePeriod) timeRange;
+ TimeRangePeriod startPeriod = rangePeriod.getStartPeriod();
+ if (startPeriod != null) {
+ values.add(Pair.of(startPeriod, StructureUtils.START_PERIOD));
+ }
+ TimeRangePeriod endPeriod = rangePeriod.getEndPeriod();
+ if (endPeriod != null) {
+ values.add(Pair.of(endPeriod, StructureUtils.END_PERIOD));
+ }
+ }
+ }
+ }
+ if (CollectionUtils.isNotEmpty(values)) {
+ jsonGenerator.writeFieldName(StructureUtils.TIME_RANGE);
+ jsonGenerator.writeStartObject();
+ for (Pair value : values) {
+ writeTimeRangePeriod(jsonGenerator, value.getLeft(), value.getRight());
+ }
+ jsonGenerator.writeEndObject();
+ }
+ }
+ }
+
+ public void writeTimeRangePeriod(JsonGenerator jsonGenerator,
+ TimeRangePeriod timeRangePeriod,
+ String timeRangePeriodName) throws IOException {
+ String period = timeRangePeriod.getPeriod();
+ boolean inclusive = timeRangePeriod.isInclusive();
+ jsonGenerator.writeFieldName(timeRangePeriodName);
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField(StructureUtils.PERIOD, period);
+ jsonGenerator.writeBooleanField(StructureUtils.IS_INCLUSIVE, inclusive);
+ jsonGenerator.writeEndObject();
+ }
+
+ private void writeValues(JsonGenerator jsonGenerator, List memberValues) throws IOException {
+ if (CollectionUtils.isNotEmpty(memberValues)) {
+ jsonGenerator.writeFieldName(StructureUtils.VALUES);
+ jsonGenerator.writeStartArray();
+ for (SelectionValue selectionValue : memberValues) {
+ if (selectionValue instanceof MemberValue) {
+ MemberValue memberValue = (MemberValue) selectionValue;
+ jsonGenerator.writeString(memberValue.getValue());
+ }
+ if (selectionValue instanceof LocalisedMemberValue) {
+ LocalisedMemberValue memberValue = (LocalisedMemberValue) selectionValue;
+ jsonGenerator.writeString(memberValue.getValue());
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/StructureUtils.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/StructureUtils.java
new file mode 100644
index 0000000..0afa33f
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/StructureUtils.java
@@ -0,0 +1,297 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.ConstraintRoleType;
+import com.epam.jsdmx.infomodel.sdmx30.FacetValueType;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalString;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClassImpl;
+import com.epam.jsdmx.infomodel.sdmx30.TextFormatImpl;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.BidiMap;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.bidimap.DualHashBidiMap;
+import org.apache.commons.lang3.StringUtils;
+
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class StructureUtils {
+
+ public static final String ID = "id";
+ public static final String NAME = "name";
+ public static final String NAMES = "names";
+ public static final String VERSION = "version";
+ public static final String AGENCY_ID = "agencyID";
+ public static final String DESCRIPTION = "description";
+ public static final String DESCRIPTIONS = "descriptions";
+ public static final String VALID_TO = "validTo";
+ public static final String VALID_FROM = "validFrom";
+
+ public static final String CATEGORIES = "categories";
+
+ public static final String ANNOTATIONS = "annotations";
+ public static final String TITLE = "title";
+ public static final String VALUE = "value";
+ public static final String TEXT = "text";
+ public static final String TEXTS = "texts";
+ public static final String LINKS = "links";
+ public static final String URN = "urn";
+ public static final String TYPE = "type";
+ public static final String REL = "rel";
+
+ public static final String IS_PARTIAL = "isPartial";
+
+ public static final String CONCEPT_IDENTITY = "conceptIdentity";
+ public static final String LOCAL_REPRESENTATION = "localRepresentation";
+ public static final String CORE_REPRESENTATION = "coreRepresentation";
+
+ public static final String SOURCE = "source";
+ public static final String TARGET = "target";
+
+ public static final String LEVEL = "level";
+
+ public static final String ATTRIBUTE_LIST = "attributeList";
+ public static final String DIMENSION_LIST = "dimensionList";
+ public static final String DIMENSIONS = "dimensions";
+ public static final String ATTACHMENT_GROUPS = "attachmentGroups";
+ public static final String GROUP_DIMENSIONS = "groupDimensions";
+ public static final String ATTRIBUTES = "attributes";
+ public static final String TIME_DIMENSIONS = "timeDimensions";
+ public static final String MEASURE_LIST = "measureList";
+ public static final String ATTRIBUTE_RELATIONSHIP = "attributeRelationship";
+ public static final String CONCEPT_ROLES = "conceptRoles";
+ public static final String POSITION = "position";
+ public static final String DIMENSION = "dimension";
+ public static final String NONE = "none";
+
+ public static final String ENUMERATION = "enumeration";
+
+
+ public static final String CONCEPTS = "concepts";
+
+ public static final String CODE = "code";
+ public static final String CODE_ID = "codeID";
+ public static final String CODES = "codes";
+ public static final String PARENT = "parent";
+ public static final String STRUCTURE = "structure";
+
+ public static final String ROLE = "role";
+
+ public static final String DATA_STRUCTURE_COMPONENTS = "dataStructureComponents";
+
+ public static final String HIERARCHICAL_CODES = "hierarchicalCodes";
+ public static final String DECIMALS = "decimals";
+ public static final String IS_MULTI_LINGUAL = "isMultiLingual";
+ public static final String IS_SEQUENCE = "isSequence";
+ public static final String INTERVAL = "interval";
+ public static final String START_VALUE = "startValue";
+ public static final String END_VALUE = "endValue";
+ public static final String TIME_INTERVAL = "timeInterval";
+ public static final String START_TIME = "startTime";
+ public static final String END_TIME = "endTime";
+ public static final String MIN_LENGTH = "minLength";
+ public static final String MAX_LENGTH = "maxLength";
+ public static final String MIN_VALUE = "minValue";
+ public static final String MAX_VALUE = "maxValue";
+ public static final String PATTERN = "pattern";
+
+ public static final String DATA = "data";
+ public static final String CATEGORISATIONS = "categorisations";
+ public static final String CATEGORY_SCHEMES = "categorySchemes";
+ public static final String CODE_LISTS = "codelists";
+ public static final String CONCEPT_SCHEMES = "conceptSchemes";
+ public static final String DATA_STRUCTURES = "dataStructures";
+ public static final String HIERARCHIES = "hierarchies";
+ public static final String HIERARCHICAL_CODELISTS = "hierarchicalCodelists";
+ public static final String DATAFLOWS = "dataflows";
+ public static final String METADATAFLOWS = "metadataflows";
+ public static final String METADATA_SETS = "metadataSets";
+ public static final String METADATA_STRUCTURES = "metadataStructures";
+
+ public static final String CODING_FORMAT = "codingFormat";
+ public static final String SCHEMA = "schema";
+ public static final String TEST = "test";
+ public static final String PREPARED = "prepared";
+ public static final String CONTENT_LANGUAGES = "contentLanguages";
+ public static final String SENDER = "sender";
+ public static final String META = "meta";
+ public static final String URI = "uri";
+
+ public static final String CONTACTS = "contacts";
+ public static final String DEPARTMENT = "department";
+ public static final String TELEPHONES = "telephones";
+ public static final String FAXES = "faxes";
+ public static final String X_400_S = "x400s";
+ public static final String URIS = "uris";
+ public static final String EMAILS = "emails";
+ public static final String AGENCIES = "agencies";
+ public static final String AGENCIES_ID = "AGENCIES";
+
+ public static final String GROUP = "group";
+ public static final String GROUPS = "groups";
+ public static final String AGENCY_SCHEMES = "agencySchemes";
+ public static final String ATTRIBUTE_DESCRIPTOR_ID = "AttributeDescriptor";
+ public static final String DIMENSION_DESCRIPTOR_ID = "DimensionDescriptor";
+ public static final String MEASURE_DESCRIPTOR_ID = "MeasureDescriptor";
+
+ public static final String CONTENT_CONSTRAINT = "contentconstraint";
+ public static final String METADATA_TARGET_REGIONS = "metadataTargetRegions";
+ public static final String RELEASE_CALENDAR = "releaseCalendar";
+ public static final String TOLERANCE = "tolerance";
+ public static final String OFFSET = "offset";
+ public static final String PERIODICITY = "periodicity";
+ public static final String CONSTRAINT_ATTACHMENT = "constraintAttachment";
+ public static final String DATA_PROVIDER = "dataProvider";
+ public static final String PROVISION_AGREEMENTS = "provisionAgreements";
+ public static final String DATA_KEY_SETS = "dataKeySets";
+ public static final String KEYS = "keys";
+ public static final String KEY_VALUES = "keyValues";
+
+ public static final String CUBE_REGIONS = "cubeRegions";
+ public static final String IS_INCLUDED = "isIncluded";
+ public static final String INCLUDE = "include";
+ public static final String VALUES = "values";
+ public static final String TIME_RANGE = "timeRange";
+ public static final String START_PERIOD = "startPeriod";
+ public static final String END_PERIOD = "endPeriod";
+ public static final String BEFORE_PERIOD = "beforePeriod";
+ public static final String AFTER_PERIOD = "afterPeriod";
+ public static final String PERIOD = "period";
+ public static final String IS_INCLUSIVE = "isInclusive";
+ public static final BidiMap CONSTRAINT_ROLE_STRING = new DualHashBidiMap<>();
+
+ public static final String IS_FINAL = "isFinal";
+
+ public static void writeInternationalString(JsonGenerator jsonGenerator,
+ InternationalString string,
+ String fieldName) throws IOException {
+ if (string != null) {
+ jsonGenerator.writeFieldName(fieldName);
+ jsonGenerator.writeStartObject();
+ string.getAllAsStream().forEach(entry -> {
+ final String text = entry.getValue();
+ final String locale = entry.getKey();
+ if (StringUtils.isNotEmpty(text)) {
+ try {
+ jsonGenerator.writeStringField(locale, text);
+ } catch (IOException e) {
+ log.error(e.getMessage());
+ }
+ }
+ });
+
+ jsonGenerator.writeEndObject();
+ }
+ }
+
+ public static String mapInstantToString(Instant time) {
+ return time == null ? null : time.toString();
+ }
+
+ public static void writeCommonFormatAttributes(JsonGenerator jsonGenerator, TextFormatImpl textFormat) throws IOException {
+ Optional valueType = textFormat.getValueType();
+ if (valueType.isPresent()) {
+ jsonGenerator.writeStringField("textType", valueType.get().value());
+ }
+ Optional sequence = textFormat.getIsSequence();
+ if (sequence.isPresent()) {
+ jsonGenerator.writeBooleanField(StructureUtils.IS_SEQUENCE, sequence.get());
+ }
+ Optional interval = textFormat.getInterval();
+ if (interval.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.INTERVAL, interval.get());
+ }
+ Optional startValue = textFormat.getStartValue();
+ if (startValue.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.START_VALUE, startValue.get());
+ }
+ Optional endValue = textFormat.getEndValue();
+ if (endValue.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.END_VALUE, endValue.get());
+ }
+ Optional timeInterval = textFormat.getTimeInterval();
+ if (timeInterval.isPresent()) {
+ jsonGenerator.writeStringField(StructureUtils.TIME_INTERVAL, timeInterval.get());
+ }
+ Optional startTime = textFormat.getStartTime();
+ if (startTime.isPresent()) {
+ jsonGenerator.writeStringField(StructureUtils.START_TIME, startTime.get().toString());
+ }
+ Optional endTime = textFormat.getEndTime();
+ if (endTime.isPresent()) {
+ jsonGenerator.writeStringField(StructureUtils.END_TIME, endTime.get().toString());
+ }
+ Optional minLength = textFormat.getMinLength();
+ if (minLength.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.MIN_LENGTH, minLength.get());
+ }
+ Optional maxLength = textFormat.getMaxLength();
+ if (maxLength.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.MAX_LENGTH, maxLength.get());
+ }
+ Optional minValue = textFormat.getMinValue();
+ if (minValue.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.MIN_VALUE, minValue.get());
+ }
+ Optional maxValue = textFormat.getMaxValue();
+ if (maxValue.isPresent()) {
+ jsonGenerator.writeNumberField(StructureUtils.MAX_VALUE, maxValue.get());
+ }
+ Optional pattern = textFormat.getPattern();
+ if (pattern.isPresent()) {
+ jsonGenerator.writeStringField(StructureUtils.PATTERN, pattern.get());
+ }
+ }
+
+ public static void writeStringField(JsonGenerator jg, String name, String value) {
+ if (value != null) {
+ try {
+ jg.writeStringField(name, value);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static List getArtefactReferencesByStructureClass(List constrainedArtefacts,
+ StructureClassImpl structureClass) {
+ return constrainedArtefacts.stream()
+ .filter(Objects::nonNull)
+ .filter(reference -> reference.getStructureClass().equals(structureClass))
+ .collect(Collectors.toList());
+ }
+
+ public static void writeArtefactReferences(JsonGenerator jsonGenerator,
+ List references,
+ String refName,
+ ReferenceAdapter adapter) throws IOException {
+ if (CollectionUtils.isNotEmpty(references)) {
+ jsonGenerator.writeFieldName(refName);
+ jsonGenerator.writeStartArray();
+ for (ArtefactReference reference : references) {
+ if (reference != null) {
+ jsonGenerator.writeString(adapter.toAdaptedUrn(reference));
+ }
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+
+ static {
+ CONSTRAINT_ROLE_STRING.put(ConstraintRoleType.ALLOWABLE_CONTENT, "Allowed");
+ CONSTRAINT_ROLE_STRING.put(ConstraintRoleType.ACTUAL_CONTENT, "Actual");
+ }
+}
diff --git a/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/VersionableWriter.java b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/VersionableWriter.java
new file mode 100644
index 0000000..b6fd983
--- /dev/null
+++ b/sdmx-json10/src/main/java/com/epam/jsdmx/json10/structure/writer/VersionableWriter.java
@@ -0,0 +1,32 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static com.epam.jsdmx.serializer.util.DateTimeConverterUtil.convertToDateTime;
+
+import java.io.IOException;
+
+import com.epam.jsdmx.infomodel.sdmx30.VersionableArtefact;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.lang3.StringUtils;
+
+public class VersionableWriter {
+
+ private final NameableWriter nameableWriter;
+
+ public VersionableWriter(NameableWriter nameableWriter) {
+ this.nameableWriter = nameableWriter;
+ }
+
+ public void write(JsonGenerator jsonGenerator, VersionableArtefact artefact) throws IOException {
+ nameableWriter.write(jsonGenerator, artefact);
+ jsonGenerator.writeStringField(StructureUtils.VERSION, artefact.getVersion().toString());
+ String validTo = convertToDateTime(artefact.getValidToString());
+ if (StringUtils.isNotBlank(validTo)) {
+ jsonGenerator.writeStringField(StructureUtils.VALID_TO, validTo);
+ }
+ String validFrom = convertToDateTime(artefact.getValidFromString());
+ if (StringUtils.isNotBlank(validFrom)) {
+ jsonGenerator.writeStringField(StructureUtils.VALID_FROM, validFrom);
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/MaintainableArtifactsTestUtils.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/MaintainableArtifactsTestUtils.java
new file mode 100644
index 0000000..00df32f
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/MaintainableArtifactsTestUtils.java
@@ -0,0 +1,1061 @@
+package com.epam.jsdmx.json10.structure;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+
+import java.io.ByteArrayOutputStream;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.AfterPeriodImpl;
+import com.epam.jsdmx.infomodel.sdmx30.AgencyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.AgencySchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Annotation;
+import com.epam.jsdmx.infomodel.sdmx30.AnnotationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.AttributeDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.BaseCodeSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.BaseFacetImpl;
+import com.epam.jsdmx.infomodel.sdmx30.BaseTextFormatRepresentationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.BeforePeriodImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CascadeValue;
+import com.epam.jsdmx.infomodel.sdmx30.CategorisationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CategoryImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CategorySchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CodeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistExtension;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistExtensionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CodingFormat;
+import com.epam.jsdmx.infomodel.sdmx30.CodingFormatImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ComponentValueImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptSchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ConstraintRoleType;
+import com.epam.jsdmx.infomodel.sdmx30.Contact;
+import com.epam.jsdmx.infomodel.sdmx30.ContactImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegionKeyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataAttributeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataConstraintImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeySet;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeySetImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataStructureDefinitionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataflowImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionComponentImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionRelationshipImpl;
+import com.epam.jsdmx.infomodel.sdmx30.EnumeratedRepresentationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ExclusiveCodeSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Facet;
+import com.epam.jsdmx.infomodel.sdmx30.FacetType;
+import com.epam.jsdmx.infomodel.sdmx30.FacetValueType;
+import com.epam.jsdmx.infomodel.sdmx30.GroupDimensionDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchicalCodeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableObjectSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.InclusiveCodeSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalString;
+import com.epam.jsdmx.infomodel.sdmx30.IsoConceptReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ItemMap;
+import com.epam.jsdmx.infomodel.sdmx30.ItemMapImpl;
+import com.epam.jsdmx.infomodel.sdmx30.LevelImpl;
+import com.epam.jsdmx.infomodel.sdmx30.LocalisedMemberValueImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureRelationshipImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelection;
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MemberValueImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataAttributeDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataAttributeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataAttributeRefImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataConstraintImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataStructureDefinitionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataTargetRegionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataflowImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ObservationRelationshipImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ProvisionAgreement;
+import com.epam.jsdmx.infomodel.sdmx30.ProvisionAgreementImpl;
+import com.epam.jsdmx.infomodel.sdmx30.RangePeriodImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ReleaseCalendar;
+import com.epam.jsdmx.infomodel.sdmx30.ReleaseCalendarImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Representation;
+import com.epam.jsdmx.infomodel.sdmx30.SentinelValueImpl;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClassImpl;
+import com.epam.jsdmx.infomodel.sdmx30.TimeDimensionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.TimeRangePeriodImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Version;
+import com.epam.jsdmx.json10.structure.writer.AnnotableWriter;
+import com.epam.jsdmx.json10.structure.writer.AttributeListWriter;
+import com.epam.jsdmx.json10.structure.writer.CategorisationWriter;
+import com.epam.jsdmx.json10.structure.writer.CategorySchemeWriter;
+import com.epam.jsdmx.json10.structure.writer.CodeWriterImpl;
+import com.epam.jsdmx.json10.structure.writer.CodelistWriter;
+import com.epam.jsdmx.json10.structure.writer.ComponentWriter;
+import com.epam.jsdmx.json10.structure.writer.ConceptRoleWriter;
+import com.epam.jsdmx.json10.structure.writer.ConceptSchemeWriter;
+import com.epam.jsdmx.json10.structure.writer.ContactsWriter;
+import com.epam.jsdmx.json10.structure.writer.DataStructureDefinitionWriter;
+import com.epam.jsdmx.json10.structure.writer.DataflowWriter;
+import com.epam.jsdmx.json10.structure.writer.DefaultHeaderProvider;
+import com.epam.jsdmx.json10.structure.writer.DimensionListWriter;
+import com.epam.jsdmx.json10.structure.writer.GroupDimensionListWriter;
+import com.epam.jsdmx.json10.structure.writer.HierarchicalCodelistWriter;
+import com.epam.jsdmx.json10.structure.writer.IdentifiableWriter;
+import com.epam.jsdmx.json10.structure.writer.Json10StructureWriter;
+import com.epam.jsdmx.json10.structure.writer.LinksWriter;
+import com.epam.jsdmx.json10.structure.writer.MeasureListWriter;
+import com.epam.jsdmx.json10.structure.writer.MetaWriter;
+import com.epam.jsdmx.json10.structure.writer.NameableWriter;
+import com.epam.jsdmx.json10.structure.writer.OrganisationWriter;
+import com.epam.jsdmx.json10.structure.writer.RepresentationWriter;
+import com.epam.jsdmx.json10.structure.writer.VersionableWriter;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+import com.epam.jsdmx.serializer.common.StubDataStructureLocalRepresentationAdapter;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+import com.epam.jsdmx.serializer.sdmx30.common.DefaultReferenceAdapter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public final class MaintainableArtifactsTestUtils {
+
+ /**
+ * sets common fields for maintainable artefact
+ *
+ * @param artefact - artefact to set fields for
+ */
+ public static void setMaintainableArtefact(MaintainableArtefactImpl artefact) {
+ artefact.setId("ARTEFACT");
+ artefact.setVersion(Version.createFromString("1.2"));
+ artefact.setOrganizationId("IMF");
+ artefact.setName(new InternationalString("Maintanable artefact"));
+ artefact.getName().add(Locale.CANADA_FRENCH.toLanguageTag(), "artefact");
+ String date = "2022-07-17T10:11:16Z";
+
+ artefact.setValidTo(date);
+ artefact.setValidFrom(date);
+ artefact.setDescription(new InternationalString("Some artefact"));
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("1");
+ annotation.setText(new InternationalString("text"));
+ annotation.setType("type1");
+ artefact.addAnnotation(annotation);
+ }
+
+ /**
+ * @return Categorisation object
+ */
+ public static CategorisationImpl buildCategorisation() {
+ CategorisationImpl categorisation = new CategorisationImpl();
+ setMaintainableArtefact(categorisation);
+ IdentifiableArtefactReferenceImpl structureItemReference = new IdentifiableArtefactReferenceImpl(
+ "ARTEFACT",
+ "IMF",
+ "1.2",
+ StructureClassImpl.CATEGORY,
+ "CAT_TARGET"
+ );
+
+ categorisation.setCategorizedBy(structureItemReference);
+ IdentifiableArtefactReferenceImpl structureReference = new IdentifiableArtefactReferenceImpl(
+ "ARTEFACT",
+ "IMF",
+ "1.2",
+ StructureClassImpl.CATEGORISATION,
+ null
+ );
+ categorisation.setCategorizedArtefact(structureReference);
+ return categorisation;
+ }
+
+ /**
+ * @return CategoryScheme object
+ */
+ public static CategorySchemeImpl buildCategoryScheme() {
+ CategorySchemeImpl categoryScheme = new CategorySchemeImpl();
+ setMaintainableArtefact(categoryScheme);
+ categoryScheme.setPartial(true);
+ CategoryImpl category = new CategoryImpl();
+ category.setDescription(new InternationalString("description"));
+ category.setId("Category-1");
+ category.setName(new InternationalString("Category1"));
+ category.setDescription(new InternationalString("description category 1"));
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("Annot");
+ category.setAnnotations(List.of(annotation));
+ CategoryImpl category2 = new CategoryImpl();
+ category2.setDescription(new InternationalString("description category2"));
+ category2.setId("Category-2");
+ category2.setName(new InternationalString("Category2"));
+ AnnotationImpl annotation2 = new AnnotationImpl();
+ annotation2.setId("Annot2");
+ category2.setAnnotations(List.of(annotation, annotation2));
+ CategoryImpl category3 = new CategoryImpl();
+ category3.setDescription(new InternationalString("description category3"));
+ category3.setId("Category-3");
+ category3.setName(new InternationalString("Category3"));
+ category.setHierarchy(List.of(category2, category3));
+
+ CategoryImpl category4 = new CategoryImpl();
+ category4.setId("Category-4");
+ category4.setName(new InternationalString("Category4"));
+ categoryScheme.setItems(List.of(category, category4));
+ return categoryScheme;
+ }
+
+ /**
+ * @return Codelist object
+ */
+ public static CodelistImpl buildCodeList() {
+ CodelistImpl codelist = new CodelistImpl();
+ MaintainableArtifactsTestUtils.setMaintainableArtefact(codelist);
+ codelist.setPartial(true);
+ codelist.setExtensions(getExtensions());
+ fillCodes(codelist);
+ return codelist;
+ }
+
+ private static void fillCodes(CodelistImpl codelist) {
+ CodeImpl code = new CodeImpl();
+ code.setDescription(new InternationalString("code 1 desc"));
+ code.setId("CODE1");
+ code.setName(new InternationalString("code 1"));
+ code.setParentId("CODE2");
+ CodeImpl code2 = new CodeImpl();
+ code2.setDescription(new InternationalString("code 2 desc"));
+ code2.setId("CODE2");
+ code2.setName(new InternationalString("code 2"));
+ code2.setHierarchy(List.of(code));
+ codelist.setItems(List.of(code, code2));
+ }
+
+ private static List getExtensions() {
+ CodelistExtensionImpl inclusiveCodeListExtension = fillInclusiveCodeListExtension();
+ CodelistExtensionImpl exclusiveCodeListExtension = fillExclusiveCodeListExtension();
+ return List.of(inclusiveCodeListExtension, exclusiveCodeListExtension);
+ }
+
+ private static CodelistExtensionImpl fillExclusiveCodeListExtension() {
+ CodelistExtensionImpl codelistExtension = new CodelistExtensionImpl();
+ ExclusiveCodeSelectionImpl exclusiveCodeSelection = new ExclusiveCodeSelectionImpl();
+ MemberValueImpl memberValue = new MemberValueImpl();
+ memberValue.setCascadeValue(CascadeValue.FALSE);
+ memberValue.setValue("mem2");
+ exclusiveCodeSelection.setMembers(List.of(memberValue));
+ return fillCodeListExtension(codelistExtension, exclusiveCodeSelection);
+ }
+
+ private static CodelistExtensionImpl fillInclusiveCodeListExtension() {
+ CodelistExtensionImpl codelistExtension = new CodelistExtensionImpl();
+ InclusiveCodeSelectionImpl inclusiveCodeSelection = new InclusiveCodeSelectionImpl();
+ MemberValueImpl memberValue = new MemberValueImpl();
+ memberValue.setCascadeValue(CascadeValue.TRUE);
+ memberValue.setValue("mem1");
+ inclusiveCodeSelection.setMembers(List.of(memberValue));
+ return fillCodeListExtension(codelistExtension, inclusiveCodeSelection);
+ }
+
+ private static CodelistExtensionImpl fillCodeListExtension(CodelistExtensionImpl codelistExtension, BaseCodeSelectionImpl codeSelection) {
+ codelistExtension.setPrefix("PREF2");
+ MaintainableArtefactReference maintainableReference = new MaintainableArtefactReference(
+ "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)");
+ codelistExtension.setCodelist(maintainableReference);
+ codelistExtension.setCodeSelection(codeSelection);
+ return codelistExtension;
+ }
+
+ /**
+ * @return ConceptScheme object
+ */
+ public static ConceptSchemeImpl buildConceptScheme() {
+ ConceptSchemeImpl conceptScheme = new ConceptSchemeImpl();
+ setMaintainableArtefact(conceptScheme);
+ ConceptImpl concept = new ConceptImpl();
+ concept.setId("COUNTRY");
+ concept.setName(new InternationalString("Country"));
+ IsoConceptReferenceImpl isoConceptReference = new IsoConceptReferenceImpl();
+ isoConceptReference.setConceptId("Conn");
+ isoConceptReference.setAgency("QHB");
+ isoConceptReference.setSchemeId("Schema");
+ concept.setIsoConceptReference(isoConceptReference);
+ concept.setCoreRepresentation(getRepresentation());
+ conceptScheme.setItems(List.of(concept));
+ conceptScheme.setPartial(true);
+ return conceptScheme;
+ }
+
+ private static Representation getRepresentation() {
+ Set facetSet = new HashSet<>();
+ BaseFacetImpl facet1 = new BaseFacetImpl();
+ BaseFacetImpl facet2 = new BaseFacetImpl();
+ facet1.setValueType(FacetValueType.ALPHA);
+ SentinelValueImpl sentinelValue = new SentinelValueImpl();
+ sentinelValue.setValue("val");
+ sentinelValue.setName(new InternationalString("sent"));
+ sentinelValue.setDescription(new InternationalString("sentinel descr"));
+ facet2.setSentinelValues(List.of(sentinelValue));
+ facet2.setType(FacetType.SENTINEL_VALUES);
+ facet2.setValueType(FacetValueType.ALPHA);
+ facetSet.addAll(List.of(facet1, facet2));
+ return new BaseTextFormatRepresentationImpl(facetSet);
+ }
+
+ /**
+ * @return DataConstraint object
+ */
+ public static DataConstraintImpl buildDataConstraint() {
+ DataConstraintImpl dataConstraint = new DataConstraintImpl();
+ setMaintainableArtefact(dataConstraint);
+ dataConstraint.setConstraintRoleType(ConstraintRoleType.ALLOWABLE_CONTENT);
+
+ LocalisedMemberValueImpl selectionValue = new LocalisedMemberValueImpl();
+ selectionValue.setValue("selectionValue local");
+ selectionValue.setLocale("en");
+
+ MemberValueImpl selectionValue1 = new MemberValueImpl();
+ selectionValue1.setValue("selectionValue1");
+
+ MemberSelectionImpl memberSelection1 = new MemberSelectionImpl();
+ memberSelection1.setSelectionValues(List.of(selectionValue, selectionValue1));
+ memberSelection1.setComponentId("some component id 1");
+ memberSelection1.setIncluded(true);
+ memberSelection1.setRemovePrefix(true);
+
+ MemberValueImpl selectionValue2 = new MemberValueImpl();
+ selectionValue2.setValue("selectionValue2");
+ selectionValue2.setCascadeValue(CascadeValue.TRUE);
+ selectionValue2.setValidTo(Instant.parse("2020-05-12T12:00:00.000000Z"));
+ selectionValue2.setValidFrom(Instant.parse("2020-04-12T12:00:00.000000Z"));
+ MemberValueImpl selectionValue5 = new MemberValueImpl();
+ selectionValue5.setValue("selectionValue5");
+
+ MemberSelectionImpl memberSelection2 = new MemberSelectionImpl();
+ memberSelection2.setSelectionValues(List.of(selectionValue2, selectionValue5));
+ memberSelection2.setComponentId("some component id 2");
+ memberSelection2.setIncluded(true);
+ memberSelection2.setRemovePrefix(true);
+
+ BeforePeriodImpl selectionValue3 = new BeforePeriodImpl();
+ selectionValue3.setPeriod("2020");
+ AfterPeriodImpl selectionValue4 = new AfterPeriodImpl();
+ selectionValue4.setPeriod("2021");
+
+ CubeRegionKeyImpl cubeRegionKey1 = new CubeRegionKeyImpl();
+ cubeRegionKey1.setComponentId("123");
+ cubeRegionKey1.setIncluded(true);
+ cubeRegionKey1.setRemovePrefix(true);
+ cubeRegionKey1.setValidFrom(Instant.parse("2020-04-12T12:00:00.000000Z"));
+ cubeRegionKey1.setValidTo(Instant.parse("2022-04-12T12:00:00.000000Z"));
+ cubeRegionKey1.setSelectionValues(List.of(selectionValue3, selectionValue4));
+
+ CubeRegionImpl cubeRegion = new CubeRegionImpl();
+ cubeRegion.setIncluded(true);
+ cubeRegion.setMemberSelections(List.of(memberSelection1, memberSelection2));
+ cubeRegion.setCubeRegionKeys(List.of(cubeRegionKey1));
+ cubeRegion.setAnnotations(getAnnotations());
+
+ dataConstraint.setCubeRegions(List.of(cubeRegion));
+ dataConstraint.setReleaseCalendar(createReleaseCalendar());
+ dataConstraint.setDataContentKeys(List.of(createDataKeySets()));
+ dataConstraint.setConstrainedArtefacts(List.of(buildProvisionAgreement().getDataProvider()));
+
+ return dataConstraint;
+ }
+
+ /**
+ * @return MetaDataConstraint object
+ */
+ public static MetadataConstraintImpl buildMetadataConstraint() {
+ MetadataConstraintImpl metadataConstraint = new MetadataConstraintImpl();
+ setMaintainableArtefact(metadataConstraint);
+ metadataConstraint.setConstraintRoleType(ConstraintRoleType.ALLOWABLE_CONTENT);
+ metadataConstraint.setReleaseCalendar(createReleaseCalendar());
+
+ IdentifiableArtefactReferenceImpl maintainableArtefactReference = new IdentifiableArtefactReferenceImpl(
+ "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)");
+ metadataConstraint.setConstrainedArtefacts(List.of(buildProvisionAgreement().getDataProvider(), maintainableArtefactReference));
+
+ MetadataTargetRegionImpl metadataTargetRegion = new MetadataTargetRegionImpl();
+ metadataTargetRegion.setIncluded(true);
+ List memberSelectionList = getMemberSelections(false, true);
+ metadataTargetRegion.setMemberSelections(memberSelectionList);
+ metadataConstraint.setMetadataTargetRegions(List.of(metadataTargetRegion));
+
+ return metadataConstraint;
+ }
+
+ /**
+ * @return DataStructureDefinition object
+ */
+ public static DataStructureDefinitionImpl buildDataStructureDefinition() {
+ DataStructureDefinitionImpl dataStructureDefinition = new DataStructureDefinitionImpl();
+ MaintainableArtifactsTestUtils.setMaintainableArtefact(dataStructureDefinition);
+ MaintainableArtefactReference maintainableReference = new MaintainableArtefactReference(
+ "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)");
+ dataStructureDefinition.setMetadataStructure(maintainableReference);
+ dataStructureDefinition.setDimensionDescriptor(fillDimensionDescriptor());
+ dataStructureDefinition.setAttributeDescriptor(fillAttributeDescriptor());
+ dataStructureDefinition.setMeasureDescriptor(fillMeasureDescriptor());
+ dataStructureDefinition.setGroupDimensionDescriptor(fillGroupDimensionDescriptor());
+ return dataStructureDefinition;
+ }
+
+ /**
+ * @return MetadataStructureDefinition object
+ */
+ public static MetadataStructureDefinitionImpl buildMetadataStructureDefinition() {
+ MetadataStructureDefinitionImpl metadataStructureDefinition = new MetadataStructureDefinitionImpl();
+ setMaintainableArtefact(metadataStructureDefinition);
+ MetadataAttributeDescriptorImpl metadataAttributeDescriptor = new MetadataAttributeDescriptorImpl();
+ metadataAttributeDescriptor.setId("MetadataAttributeDescriptor");
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("Annot-1");
+ MetadataAttributeImpl metadataAttribute = getMetadataAttribute("meta-1", "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)");
+ metadataAttribute.setHierarchy(List.of(getMetadataAttribute("meta-2", "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)")));
+ metadataAttributeDescriptor.setComponents(List.of(metadataAttribute));
+ metadataAttributeDescriptor.addAnnotation(annotation);
+ metadataStructureDefinition.setAttributeDescriptor(metadataAttributeDescriptor);
+ return metadataStructureDefinition;
+ }
+
+ private static MetadataAttributeImpl getMetadataAttribute(String id, String reference) {
+ MetadataAttributeImpl metadataAttribute = new MetadataAttributeImpl();
+ metadataAttribute.setId(id);
+ metadataAttribute.setMaxOccurs(20);
+ metadataAttribute.setMinOccurs(10);
+ metadataAttribute.setPresentational(true);
+ Representation representation = new EnumeratedRepresentationImpl(
+ new MaintainableArtefactReference(reference));
+ metadataAttribute.setLocalRepresentation(representation);
+ metadataAttribute.setConceptIdentity(
+ new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DIM"));
+ return metadataAttribute;
+ }
+
+ private static List fillGroupDimensionDescriptor() {
+ GroupDimensionDescriptorImpl d = new GroupDimensionDescriptorImpl();
+ d.setId("GROUP1");
+ d.setDimensions(List.of("DIM1"));
+ return List.of(d);
+ }
+
+ /**
+ * @return AttributeDescriptor
+ */
+ public static AttributeDescriptorImpl fillAttributeDescriptor() {
+ AttributeDescriptorImpl attributeDescriptor = new AttributeDescriptorImpl();
+ attributeDescriptor.setId("AttributeDescriptor");
+ fillMetadata(attributeDescriptor);
+ fillComponents(attributeDescriptor);
+ return attributeDescriptor;
+ }
+
+
+ private static void fillComponents(AttributeDescriptorImpl attributeDescriptor) {
+ DataAttributeImpl dataAttribute = new DataAttributeImpl();
+ dataAttribute.setId("DA1");
+ dataAttribute.setMinOccurs(0);
+ DimensionRelationshipImpl rel = new DimensionRelationshipImpl();
+ rel.setDimensions(List.of("DIM1"));
+ rel.setGroupKeys(List.of("GROUP1"));
+ dataAttribute.setAttributeRelationship(rel);
+ BaseFacetImpl baseFacet = new BaseFacetImpl();
+ baseFacet.setValueType(FacetValueType.ALPHA_NUMERIC);
+ dataAttribute.setLocalRepresentation(new BaseTextFormatRepresentationImpl(Set.of(baseFacet)));
+ dataAttribute.setConceptIdentity(new IdentifiableArtefactReferenceImpl(
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE"));
+ dataAttribute.setConceptRoles(
+ List.of(
+ new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"),
+ new IdentifiableArtefactReferenceImpl(
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE")
+ ));
+ MeasureRelationshipImpl measureRelationship = new MeasureRelationshipImpl();
+ measureRelationship.setMeasures(List.of("MO"));
+ dataAttribute.setMeasureRelationship(measureRelationship);
+ attributeDescriptor.setComponents(List.of(dataAttribute));
+ }
+
+ private static void fillMetadata(AttributeDescriptorImpl attributeDescriptor) {
+ MetadataAttributeRefImpl attributeRef1 = new MetadataAttributeRefImpl();
+ attributeRef1.setId("REFERENCE-1");
+ attributeRef1.setMetadataRelationship(new ObservationRelationshipImpl());
+ MetadataAttributeRefImpl attributeRef2 = new MetadataAttributeRefImpl();
+ attributeRef2.setId("REFERENCE-2");
+ DimensionRelationshipImpl dimensionRelationship = new DimensionRelationshipImpl();
+ dimensionRelationship.setDimensions(List.of("dim1", "dim2"));
+ attributeRef2.setMetadataRelationship(dimensionRelationship);
+ MetadataAttributeRefImpl attributeRef3 = new MetadataAttributeRefImpl();
+ attributeRef3.setId("REFERENCE-3");
+ attributeRef3.setMetadataRelationship(null);
+ attributeDescriptor.setMetadataAttributes(List.of(attributeRef1, attributeRef2, attributeRef3));
+ }
+
+ /**
+ * @return List
+ */
+ public static List fillListGroupDimensionDescriptor() {
+ GroupDimensionDescriptorImpl groupDimensionDescriptor1 = new GroupDimensionDescriptorImpl();
+ AnnotationImpl annotation1 = new AnnotationImpl();
+ annotation1.setId("id1");
+ annotation1.setValue("val");
+ AnnotationImpl annotation2 = new AnnotationImpl();
+ annotation2.setId("id2");
+ annotation2.setValue("val");
+ groupDimensionDescriptor1.setId("GroupDimensionDescriptor1");
+ groupDimensionDescriptor1.setAnnotations(List.of(annotation1, annotation2));
+ groupDimensionDescriptor1.setDimensions(List.of("dim1", "dim12"));
+
+ GroupDimensionDescriptorImpl groupDimensionDescriptor2 = new GroupDimensionDescriptorImpl();
+ groupDimensionDescriptor2.setId("GroupDimensionDescriptor2");
+ groupDimensionDescriptor2.setDimensions(List.of("dim21", "dim22"));
+
+ return List.of(groupDimensionDescriptor1, groupDimensionDescriptor2);
+ }
+
+ /**
+ * @return DimensionDescriptor
+ */
+ public static DimensionDescriptorImpl fillDimensionDescriptor() {
+ DimensionDescriptorImpl dimensionDescriptor = new DimensionDescriptorImpl();
+ dimensionDescriptor.setId("DimensionDescriptor");
+ fillDimensionComponents(dimensionDescriptor);
+ return dimensionDescriptor;
+ }
+
+ private static void fillDimensionComponents(DimensionDescriptorImpl dimensionDescriptor) {
+ DimensionImpl dimensionComponent = new DimensionImpl();
+ dimensionComponent.setOrder(1);
+ dimensionComponent.setId("DIM1");
+ dimensionComponent.setConceptRoles(List.of(new IdentifiableArtefactReferenceImpl(
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE")));
+ EnumeratedRepresentationImpl enumeratedRepresentation = new EnumeratedRepresentationImpl(
+ new MaintainableArtefactReference("urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"));
+ dimensionComponent.setLocalRepresentation(enumeratedRepresentation);
+ dimensionComponent.setConceptIdentity(
+ new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE"));
+ DimensionComponentImpl dimensionTimeComponent = new TimeDimensionImpl();
+ dimensionTimeComponent.setId("TIME_PERIOD");
+ dimensionTimeComponent.setOrder(2);
+ dimensionTimeComponent.setConceptIdentity(
+ new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE"));
+ BaseFacetImpl baseFacet = new BaseFacetImpl();
+ baseFacet.setValueType(FacetValueType.BASIC_TIME_PERIOD);
+ dimensionTimeComponent.setLocalRepresentation(new BaseTextFormatRepresentationImpl(Set.of(baseFacet)));
+ dimensionDescriptor.setComponents(List.of(dimensionComponent, dimensionTimeComponent));
+ }
+
+ /**
+ * @return MeasureDescriptor
+ */
+ public static MeasureDescriptorImpl fillMeasureDescriptor() {
+ MeasureDescriptorImpl measureDescriptor = new MeasureDescriptorImpl();
+ measureDescriptor.setId("MeasureDescriptor");
+ fillMeasureComponents(measureDescriptor);
+ return measureDescriptor;
+ }
+
+ private static void fillMeasureComponents(MeasureDescriptorImpl measureDescriptor) {
+ MeasureImpl measure = new MeasureImpl();
+ measure.setId("MES1");
+ measure.setConceptIdentity(new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE"));
+ measure.setConceptRoles(
+ List.of(
+ new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"),
+ new IdentifiableArtefactReferenceImpl(
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE")
+ ));
+ BaseFacetImpl baseFacet = new BaseFacetImpl();
+ baseFacet.setValueType(FacetValueType.BASIC_TIME_PERIOD);
+ measure.setLocalRepresentation(new BaseTextFormatRepresentationImpl(Set.of(baseFacet)));
+ measureDescriptor.setComponents(List.of(measure));
+ }
+
+ /**
+ * @return Hierarchy object
+ */
+ public static HierarchyImpl buildHierarchy() {
+ HierarchyImpl hierarchy = new HierarchyImpl();
+ MaintainableArtifactsTestUtils.setMaintainableArtefact(hierarchy);
+ hierarchy.setHasFormalLevels(true);
+ HierarchicalCodeImpl code = new HierarchicalCodeImpl();
+ code.setId("id");
+ code.setCode(new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1"));
+ code.setLevelId("Lev1");
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("id");
+ code.setAnnotations(List.of(annotation));
+ String dateStr = "2022-07-17T10:11:16.345Z";
+ code.setValidTo(Instant.parse(dateStr));
+ code.setValidFrom(Instant.parse(dateStr));
+ HierarchicalCodeImpl code2 = new HierarchicalCodeImpl();
+ code2.setId("hi-2");
+ code2.setLevelId("Lev2");
+ code2.setCode(new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL2"));
+ code2.setValidTo(Instant.parse(dateStr));
+ code2.setValidFrom(Instant.parse(dateStr));
+ hierarchy.setCodes(List.of(code, code2));
+ LevelImpl level = new LevelImpl();
+ level.setDescription(new InternationalString("hierarchy desc"));
+ level.setName(new InternationalString("hierarchy name"));
+ level.setId("levId");
+ level.setCodeFormat(List.of(fillListOfCodingFormats().get(0)));
+ hierarchy.setLevel(level);
+ return hierarchy;
+ }
+
+ public static HierarchyImpl buildHierarchyWithInnerHierarchy() {
+ HierarchyImpl hierarchy = new HierarchyImpl();
+ MaintainableArtifactsTestUtils.setMaintainableArtefact(hierarchy);
+ hierarchy.setHasFormalLevels(true);
+ HierarchicalCodeImpl code = new HierarchicalCodeImpl();
+ code.setId("id");
+ code.setCode(new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1"));
+ code.setLevelId("Lev1");
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("id");
+ code.setAnnotations(List.of(annotation));
+ String dateStr = "2022-07-17T10:11:16.345Z";
+ code.setValidTo(Instant.parse(dateStr));
+ code.setValidFrom(Instant.parse(dateStr));
+ HierarchicalCodeImpl hierarchicalCode = new HierarchicalCodeImpl();
+ hierarchicalCode.setCode(new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1"));
+ hierarchicalCode.setId("1");
+ hierarchicalCode.setValidTo(Instant.parse(dateStr));
+ hierarchicalCode.setValidFrom(Instant.parse(dateStr));
+ code.setHierarchicalCodes(List.of(hierarchicalCode));
+ hierarchy.setCodes(List.of(code));
+ LevelImpl level = new LevelImpl();
+ level.setDescription(new InternationalString("hierarchy desc"));
+ level.setName(new InternationalString("hierarchy name"));
+ level.setId("levId");
+ level.setCodeFormat(List.of(fillListOfCodingFormats().get(0)));
+ hierarchy.setLevel(level);
+ LevelImpl levelUn = new LevelImpl();
+ levelUn.setId("levUn");
+ levelUn.setName(new InternationalString("underLevel"));
+ level.setChild(levelUn);
+ return hierarchy;
+ }
+
+ private static List fillListOfCodingFormats() {
+ List codingFormatList = new ArrayList<>();
+ codingFormatList.add(addCodingFormat(FacetType.INTERVAL, String.valueOf(1)));
+ codingFormatList.add(addCodingFormat(FacetType.IS_SEQUENCE, String.valueOf(true)));
+ codingFormatList.add(addCodingFormat(FacetType.MAX_LENGTH, String.valueOf(1)));
+ codingFormatList.add(addCodingFormat(FacetType.MIN_LENGTH, String.valueOf(1)));
+ codingFormatList.add(addCodingFormat(FacetType.MAX_VALUE, String.valueOf(1)));
+ codingFormatList.add(addCodingFormat(FacetType.MIN_VALUE, String.valueOf(1)));
+ codingFormatList.add(addCodingFormat(FacetType.PATTERN, "a"));
+ codingFormatList.add(addCodingFormat(FacetType.START_VALUE, String.valueOf(1)));
+ codingFormatList.add(addCodingFormat(FacetType.END_VALUE, String.valueOf(1)));
+
+ CodingFormatImpl codingFormat = new CodingFormatImpl();
+ codingFormat.setCodingFormat(new BaseFacetImpl(FacetValueType.fromValue("Alpha")));
+ codingFormatList.add(codingFormat);
+ return codingFormatList;
+ }
+
+ private static CodingFormat addCodingFormat(FacetType facetType, String value) {
+ CodingFormatImpl codingFormat = new CodingFormatImpl();
+ BaseFacetImpl baseFacet = new BaseFacetImpl();
+ baseFacet.setType(facetType);
+ baseFacet.setValue(value);
+ codingFormat.setCodingFormat(baseFacet);
+ return codingFormat;
+ }
+
+ /**
+ * @return Metadataflow object
+ */
+ public static MetadataflowImpl buildMetadataFlow() {
+ MetadataflowImpl metadataflow = new MetadataflowImpl();
+ setMaintainableArtefact(metadataflow);
+ metadataflow.setStructure(new MaintainableArtefactReference("urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=IMF:METADATA(1.0.0)"));
+ IdentifiableObjectSelectionImpl objectSelection = new IdentifiableObjectSelectionImpl();
+ MaintainableArtefactReference maintainableArtefactReference = new MaintainableArtefactReference(
+ "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.2)");
+
+ MaintainableArtefactReference maintainableArtefactReference2 = new MaintainableArtefactReference(
+ "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.3)");
+ objectSelection.setResolvesTo(List.of(maintainableArtefactReference, maintainableArtefactReference2));
+ metadataflow.setSelections(List.of(objectSelection));
+ return metadataflow;
+ }
+
+ /**
+ * @return Dataflow object
+ */
+ public static DataflowImpl buildDataFlow() {
+ DataflowImpl dataflow = new DataflowImpl();
+ dataflow.setId("DF");
+ dataflow.setStructure(new MaintainableArtefactReference("urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.2)"));
+ setMaintainableArtefact(dataflow);
+ return dataflow;
+ }
+
+ /**
+ * @return AgencyScheme
+ */
+
+ public static AgencySchemeImpl buildAgencyScheme() {
+ AgencySchemeImpl agencyScheme = new AgencySchemeImpl();
+ MaintainableArtifactsTestUtils.setMaintainableArtefact(agencyScheme);
+ agencyScheme.setId("AGENCIES");
+ agencyScheme.setOrganizationId("SDMX");
+ agencyScheme.setVersion(Version.createFromString("1.0"));
+ AgencyImpl agency1 = createAgency("AG-1");
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("Annot");
+ agency1.setAnnotations(List.of(annotation));
+ AgencyImpl agency2 = createAgency("AG-2");
+
+
+ agencyScheme.setItems(List.of(agency1, agency2));
+ return agencyScheme;
+ }
+
+ private static AgencyImpl createAgency(String id) {
+ AgencyImpl agency1 = new AgencyImpl();
+ agency1.setId(id);
+ agency1.setName(new InternationalString("Agency"));
+ agency1.setDescription(new InternationalString("Agency"));
+
+ List contacts = getContacts();
+
+ agency1.setContacts(contacts);
+ return agency1;
+ }
+
+ private static List getContacts() {
+ ContactImpl contact1 = new ContactImpl();
+ contact1.setName("AG_1");
+ contact1.setTelephone("9458673082");
+ contact1.setFax("somedatafax");
+ contact1.setEmail("test@email.com");
+ contact1.setOrganizationUnit("IT");
+ Map resp = new HashMap<>();
+ resp.put("en", "writing");
+ resp.put("fr", "ecrire");
+ contact1.setResponsibility(new InternationalString(resp));
+ contact1.setX400("X400");
+
+ ContactImpl contact2 = new ContactImpl();
+ contact2.setName("AG_2");
+ contact2.setTelephone("9458673082");
+ contact2.setFax("somedatafax2");
+ contact2.setEmail("test@email.com");
+ contact2.setOrganizationUnit("IT");
+ contact2.setResponsibility(new InternationalString(resp));
+ contact2.setX400("X400");
+ return List.of(contact1, contact2);
+ }
+
+ private static ItemMap createItemMap(String target, String source) {
+ ItemMapImpl itemMap = new ItemMapImpl();
+ itemMap.setTarget(target);
+ itemMap.setSource(source);
+ itemMap.setValidTo(Instant.parse("2022-07-17T00:00:00Z"));
+ itemMap.setValidFrom(Instant.parse("2022-07-17T00:00:00Z"));
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("Ann");
+ itemMap.setAnnotations(List.of(annotation));
+ return itemMap;
+ }
+
+ public static VersionableWriter getVersionableWriter() {
+ final NameableWriter nameableWriter = getNameableWriter();
+ return new VersionableWriter(nameableWriter);
+ }
+
+ public static NameableWriter getNameableWriter() {
+ final IdentifiableWriter identifiableWriter = getIdentifiableWriter();
+ return new NameableWriter(identifiableWriter);
+ }
+
+ public static IdentifiableWriter getIdentifiableWriter() {
+ final AnnotableWriter annotableWriter = getAnnotableWriter();
+ return new IdentifiableWriter(annotableWriter);
+ }
+
+ public static AnnotableWriter getAnnotableWriter() {
+ return new AnnotableWriter(getLinksWriter());
+ }
+
+ public static RepresentationWriter getRepresentationWriter() {
+ return new RepresentationWriter(getReferenceResolver(), new DefaultReferenceAdapter());
+ }
+
+ public static ComponentWriter getComponentWriter() {
+ return new ComponentWriter(getIdentifiableWriter(), getRepresentationWriter(), getReferenceResolver());
+ }
+
+ public static ConceptRoleWriter getConceptRoleWriter() {
+ return new ConceptRoleWriter(getReferenceResolver());
+ }
+
+ public static AttributeListWriter getAttributeListWriter() {
+ return new AttributeListWriter(getIdentifiableWriter(), getConceptRoleWriter(), getComponentWriter());
+ }
+
+ public static DimensionListWriter getDimensionListWriter() {
+ return new DimensionListWriter(getIdentifiableWriter(), getConceptRoleWriter(), getComponentWriter());
+ }
+
+ public static GroupDimensionListWriter getGroupDimensionListWriter() {
+ return new GroupDimensionListWriter(getIdentifiableWriter());
+ }
+
+ public static MeasureListWriter getMeasureListWriter() {
+ return new MeasureListWriter(getIdentifiableWriter(), getConceptRoleWriter(), getComponentWriter());
+ }
+
+ public static LinksWriter getLinksWriter() {
+ return new LinksWriter(getReferenceAdapter());
+ }
+
+ public static ContactsWriter getContactsWriter() {
+ return new ContactsWriter();
+ }
+
+ public static OrganisationWriter getOrganisationWriter() {
+ return new OrganisationWriter(getContactsWriter(), getNameableWriter());
+ }
+
+ public static Json10StructureWriter createStructureWriter(
+ ByteArrayOutputStream outputStream,
+ ObjectMapper mapper,
+ DefaultHeaderProvider defaultHeaderProvider) {
+ ReferenceResolver res = getReferenceResolver();
+ final var referenceAdapter = new DefaultReferenceAdapter();
+ final var linksWriter = new LinksWriter(referenceAdapter);
+ final var headerWriter = new MetaWriter(List.of(), defaultHeaderProvider);
+ final var representationWriter = new RepresentationWriter(res, referenceAdapter);
+ final var annotableWriter = new AnnotableWriter(linksWriter);
+ final var identifiableWriter = new IdentifiableWriter(annotableWriter);
+ final var nameableWriter = new NameableWriter(identifiableWriter);
+ final var versionableWriter = new VersionableWriter(nameableWriter);
+ final var componentWriter = new ComponentWriter(identifiableWriter, representationWriter, res);
+ final var conceptRoleWriter = new ConceptRoleWriter(res);
+ final var attributeListWriter = new AttributeListWriter(identifiableWriter, conceptRoleWriter, componentWriter);
+ final var dimensionListWriter = new DimensionListWriter(identifiableWriter, conceptRoleWriter, componentWriter);
+ final var measureListWriter = new MeasureListWriter(identifiableWriter, conceptRoleWriter, componentWriter);
+ final var groupDimensionListWriter = new GroupDimensionListWriter(identifiableWriter);
+ final var categorisationWriter = new CategorisationWriter(versionableWriter, linksWriter, res, referenceAdapter);
+ final var categorySchemeWriter = new CategorySchemeWriter(versionableWriter, linksWriter, nameableWriter);
+ final var conceptSchemeWriter = new ConceptSchemeWriter(versionableWriter, linksWriter, nameableWriter, representationWriter);
+ final var codeImplWriter = new CodeWriterImpl();
+ final var codeListWriter = new CodelistWriter(versionableWriter, linksWriter, codeImplWriter, nameableWriter);
+ final var dataFlowWriter = new DataflowWriter(versionableWriter, linksWriter, res);
+ final var dataStructureDefinitionWriter = new DataStructureDefinitionWriter(
+ versionableWriter,
+ linksWriter,
+ attributeListWriter,
+ dimensionListWriter,
+ measureListWriter,
+ groupDimensionListWriter,
+ dsd -> dsd,
+ new StubDataStructureLocalRepresentationAdapter()
+ );
+ final var hierarchyWriter = new HierarchicalCodelistWriter(versionableWriter, linksWriter, identifiableWriter, nameableWriter, res, referenceAdapter);
+
+ return new Json10StructureWriter(
+ mapper,
+ outputStream,
+ headerWriter,
+ List.of(categorisationWriter,
+ categorySchemeWriter,
+ codeListWriter,
+ conceptSchemeWriter,
+ dataStructureDefinitionWriter,
+ hierarchyWriter,
+ dataFlowWriter
+ )
+ );
+ }
+
+ public static void assertMaintainableArtefactsInJson(String json) {
+ assertThatJson(json).isPresent();
+ assertThatJson(json).node("id")
+ .isString()
+ .isEqualTo("ARTEFACT");
+ assertThatJson(json).node("version")
+ .isString()
+ .isEqualTo("1.2");
+ assertThatJson(json).node("agencyID")
+ .isString()
+ .isEqualTo("IMF");
+ assertThatJson(json).node("name")
+ .isString()
+ .isEqualTo("Maintanable artefact");
+ assertThatJson(json).node("names")
+ .isPresent()
+ .isObject()
+ .containsEntry("en", "Maintanable artefact");
+ assertThatJson(json).node("names")
+ .isPresent()
+ .isObject()
+ .containsEntry("fr-CA", "artefact");
+ assertThatJson(json).node("description")
+ .isString()
+ .isEqualTo("Some artefact");
+ assertThatJson(json).node("descriptions")
+ .isPresent()
+ .isObject()
+ .containsEntry("en", "Some artefact");
+ assertThatJson(json).node("annotations")
+ .isArray()
+ .element(0)
+ .node("type")
+ .isEqualTo("type1");
+ assertThatJson(json).node("annotations")
+ .isArray()
+ .element(0)
+ .node("id")
+ .isString()
+ .isEqualTo("1");
+ }
+
+ private static List getMemberSelections(boolean isSimple, boolean isJson) {
+ MemberSelectionImpl memberSelection = new MemberSelectionImpl();
+ memberSelection.setIncluded(false);
+ memberSelection.setRemovePrefix(true);
+ memberSelection.setComponentId("MS");
+
+ MemberValueImpl memberValue;
+ if (isSimple) {
+ memberValue = getMemberValueSimple();
+ } else {
+ memberValue = getMemberValue();
+ }
+
+ LocalisedMemberValueImpl localisedMemberValue = new LocalisedMemberValueImpl();
+ localisedMemberValue.setValue("local");
+ localisedMemberValue.setLocale("en");
+
+ memberSelection.setSelectionValues(List.of(memberValue, localisedMemberValue));
+
+ MemberSelectionImpl memberSelection2 = new MemberSelectionImpl();
+ memberSelection2.setIncluded(false);
+ memberSelection2.setRemovePrefix(true);
+ memberSelection2.setComponentId("MS2");
+ BeforePeriodImpl beforePeriod = new BeforePeriodImpl();
+ beforePeriod.setInclusive(true);
+ beforePeriod.setPeriod("2023");
+ if (!isJson) {
+ beforePeriod.setValidFrom(Instant.parse("2022-07-17T10:11:16Z"));
+ beforePeriod.setValidTo(Instant.parse("2022-09-17T10:11:16Z"));
+ }
+
+ AfterPeriodImpl afterPeriod = new AfterPeriodImpl();
+ afterPeriod.setInclusive(false);
+ afterPeriod.setPeriod("2028");
+ afterPeriod.setValidFrom(Instant.parse("2022-07-17T10:11:16Z"));
+ afterPeriod.setValidTo(Instant.parse("2022-09-17T10:11:16Z"));
+
+ RangePeriodImpl rangePeriod = new RangePeriodImpl();
+ TimeRangePeriodImpl timeRangePeriod = new TimeRangePeriodImpl();
+ timeRangePeriod.setInclusive(true);
+ timeRangePeriod.setPeriod("2022");
+ timeRangePeriod.setValidTo(Instant.parse("2022-09-17T10:11:16Z"));
+ timeRangePeriod.setValidFrom(Instant.parse("2022-07-17T10:11:16Z"));
+
+ rangePeriod.setStartPeriod(timeRangePeriod);
+ timeRangePeriod.setInclusive(false);
+ rangePeriod.setEndPeriod(timeRangePeriod);
+ rangePeriod.setValidFrom(Instant.parse("2022-07-17T10:11:16Z"));
+ rangePeriod.setValidTo(Instant.parse("2022-09-17T10:11:16Z"));
+
+ memberSelection2.setSelectionValues(List.of(beforePeriod));
+ return List.of(memberSelection, memberSelection2);
+ }
+
+ private static MemberValueImpl getMemberValue() {
+ MemberValueImpl memberValue = new MemberValueImpl();
+ memberValue.setValue("MEM");
+ memberValue.setCascadeValue(CascadeValue.EXCLUDE_ROOT);
+ memberValue.setValidFrom(Instant.parse("2022-07-17T10:11:16Z"));
+ memberValue.setValidTo(Instant.parse("2022-09-17T10:11:16Z"));
+ return memberValue;
+ }
+
+ private static MemberValueImpl getMemberValueSimple() {
+ MemberValueImpl memberValue = new MemberValueImpl();
+ memberValue.setValue("MEM");
+ return memberValue;
+ }
+
+ private static ReleaseCalendar createReleaseCalendar() {
+ ReleaseCalendarImpl releaseCalendar = new ReleaseCalendarImpl();
+ releaseCalendar.setOffset("Offset");
+ releaseCalendar.setTolerance("Tolerance");
+ releaseCalendar.setPeriodicity("Periodicity");
+ return releaseCalendar;
+ }
+
+ private static DataKeySet createDataKeySets() {
+ DataKeySetImpl dataKeySet = new DataKeySetImpl();
+ dataKeySet.setIncluded(true);
+ DataKeyImpl dataKey = new DataKeyImpl();
+ dataKey.setIncluded(true);
+ ComponentValueImpl componentValue = new ComponentValueImpl();
+ componentValue.setIncluded(true);
+ componentValue.setComponentId("C1");
+ componentValue.setRemovePrefix(true);
+ componentValue.setValue("Value");
+ dataKey.setKeyValues(List.of(componentValue));
+ dataKey.setMemberSelections(getMemberSelections(true, true));
+ dataKey.setValidFrom(Instant.parse("2022-07-17T10:11:16Z"));
+ dataKey.setValidTo(Instant.parse("2022-07-17T10:11:16Z"));
+ dataKeySet.setKeys(List.of(dataKey));
+
+ return dataKeySet;
+ }
+
+ public static ProvisionAgreement buildProvisionAgreement() {
+ ProvisionAgreementImpl provisionAgreement = new ProvisionAgreementImpl();
+ setMaintainableArtefact(provisionAgreement);
+ provisionAgreement.setControlledStructureUsage(
+ new MaintainableArtefactReference("urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=IMF:ARTEFACT(1.2)")
+ );
+ provisionAgreement.setDataProvider(
+ new IdentifiableArtefactReferenceImpl("urn:sdmx:org.sdmx.infomodel.base.DataProvider=IMF:DATA_PROVIDERS(1.0).DP")
+ );
+ return provisionAgreement;
+ }
+
+ private static List getAnnotations() {
+ AnnotationImpl annotation = new AnnotationImpl();
+ annotation.setId("Annot");
+ return List.of(annotation);
+ }
+
+ public static CodeWriterImpl getCodeImplWriter() {
+ return new CodeWriterImpl();
+ }
+
+ public static ReferenceResolver getReferenceResolver() {
+ // this should be anonymous class, but it is not possible to mock lambda
+ return new ReferenceResolver() {
+ @Override
+ public ArtefactReference resolve(ArtefactReference reference) {
+ return reference;
+ }
+ };
+ }
+
+ public static ReferenceAdapter getReferenceAdapter() {
+ return new DefaultReferenceAdapter();
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/SdmxSourceCompatibilityTester.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/SdmxSourceCompatibilityTester.java
new file mode 100644
index 0000000..ecee429
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/SdmxSourceCompatibilityTester.java
@@ -0,0 +1,57 @@
+package com.epam.jsdmx.json10.structure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+import java.util.Set;
+import java.util.function.Function;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.sdmxsource.sdmx.api.factory.StructureParserFactory;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+import org.sdmxsource.sdmx.api.model.beans.base.MaintainableBean;
+import org.sdmxsource.sdmx.api.util.ReadableDataLocation;
+import org.sdmxsource.sdmx.sdmxbeans.model.SdmxStructureJsonFormat;
+import org.sdmxsource.sdmx.structureparser.builder.sdmxbeans.impl.SdmxObjectsJsonV1Builder;
+import org.sdmxsource.sdmx.structureparser.factory.SdmxJsonStructureParserFactory;
+import org.sdmxsource.util.factory.SdmxSourceReadableDataLocationFactory;
+
+public class SdmxSourceCompatibilityTester {
+
+ public void test(String serialized, Function> extractor) {
+ final ReadableDataLocation readableDataLocation = new SdmxSourceReadableDataLocationFactory()
+ .getReadableDataLocation(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));
+ final StructureParserFactory structureParserFactory = new SdmxJsonStructureParserFactory(new SdmxStructureJsonFormat());
+
+ registerJavaTimeModuleWithinFactor(structureParserFactory);
+
+ SdmxBeans beans = structureParserFactory.getSdmxBeans(readableDataLocation);
+ assertThat(extractor.apply(beans)).isNotEmpty();
+ }
+
+ private static void registerJavaTimeModuleWithinFactor(StructureParserFactory structureParserFactory) {
+ Field sdmxBeansJsonBuilderField = null;
+ Field objectMapperField = null;
+ try {
+ sdmxBeansJsonBuilderField = structureParserFactory.getClass().getDeclaredField("sdmxBeansJsonBuilder");
+ sdmxBeansJsonBuilderField.setAccessible(true);
+ final var sdmxBeansJsonBuilder = (SdmxObjectsJsonV1Builder) sdmxBeansJsonBuilderField.get(structureParserFactory);
+ objectMapperField = sdmxBeansJsonBuilder.getClass().getDeclaredField("objectMapper");
+ objectMapperField.setAccessible(true);
+ final var objectMapper = (ObjectMapper) objectMapperField.get(sdmxBeansJsonBuilder);
+ objectMapper.registerModule(new JavaTimeModule());
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (objectMapperField != null) {
+ objectMapperField.setAccessible(false);
+ }
+ if (sdmxBeansJsonBuilderField != null) {
+ sdmxBeansJsonBuilderField.setAccessible(false);
+ }
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/TestUtils.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/TestUtils.java
new file mode 100644
index 0000000..f6aec4e
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/TestUtils.java
@@ -0,0 +1,23 @@
+package com.epam.jsdmx.json10.structure;
+
+public class TestUtils {
+
+ public static final String CATEGORISATION_JSON = "/serialization.expected/categorisation.json";
+ public static final String CATEGORY_SCHEMA_JSON = "/serialization.expected/category-schema.json";
+ public static final String CODE_LIST_JSON = "/serialization.expected/codelist.json";
+ public static final String CONCEPT_SCHEMA_JSON = "/serialization.expected/concept-scheme.json";
+ public static final String HIERARCHY_WITH_CHILDREN_JSON = "/serialization.expected/hierarchy.json";
+ public static final String HIERARCHY_JSON = "/serialization.expected/hierarchy_flat.json";
+ public static final String DATA_FLOW_JSON = "/serialization.expected/data-flow.json";
+ public static final String METADATA_FLOW_JSON = "/serialization.expected/metadata-flow.json";
+ public static final String METADATA_STRUCTURE_DEF_JSON = "/serialization.expected/metadata-structure-definition.json";
+ public static final String DATA_CONSTRAINT_JSON = "/serialization.expected/data-constraint.json";
+ public static final String META_DATA_CONSTRAINT_JSON = "/serialization.expected/meta-data-constraint.json";
+ public static final String DATA_STRUCTURE_DEFINITION_JSON = "/serialization.expected/data-structure-definition.json";
+
+ public static final String AGENCY_SCHEME_JSON = "/serialization.expected/agency-scheme.json";
+ public static final String DATA_CONSUMER_SCHEME_JSON = "/serialization.expected/data-consumer-scheme.json";
+ public static final String DATA_PROVIDER_SCHEME_JSON = "/serialization.expected/data-provider-scheme.json";
+ public static final String ORGANISATION_UNIT_SCHEME_JSON = "/serialization.expected/organisation-unit-scheme.json";
+ public static final String ORGANISATION_SCHEME_MAP_JSON = "/serialization.expected/organisation-scheme-map.json";
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/AttributeListWriterJsonTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/AttributeListWriterJsonTest.java
new file mode 100644
index 0000000..40ec343
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/AttributeListWriterJsonTest.java
@@ -0,0 +1,90 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.AttributeDescriptorImpl;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import net.javacrumbs.jsonunit.JsonAssert;
+import net.javacrumbs.jsonunit.core.Configuration;
+import net.javacrumbs.jsonunit.core.Option;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+class AttributeListWriterJsonTest {
+
+ public static final String ATTRIBUTE_LIST_JSON = "/serialization.expected/attribute-list.json";
+ public static final String ATTRIBUTE_LIST_EMPTY_JSON = "/serialization.expected/attribute-list_empty.json";
+
+ @Spy
+ private IdentifiableWriter annotableWriterSpy = MaintainableArtifactsTestUtils.getIdentifiableWriter();
+ @Spy
+ private ComponentWriter componentWriterSpy = MaintainableArtifactsTestUtils.getComponentWriter();
+ @Spy
+ private ConceptRoleWriter conceptRoleWriterSpy = MaintainableArtifactsTestUtils.getConceptRoleWriter();
+ @InjectMocks
+ public AttributeListWriter attributeListWriterJson;
+
+ AutoCloseable closeable;
+
+ @BeforeEach
+ void setup() {
+ closeable = MockitoAnnotations.openMocks(this);
+ }
+
+ @AfterEach
+ void teardown() throws Exception {
+ closeable.close();
+ }
+
+ @Test
+ void writeAttributeListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ AttributeDescriptorImpl descriptor = MaintainableArtifactsTestUtils.fillAttributeDescriptor();
+
+ //when
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ jGenerator.writeStartObject();
+ try (jGenerator) {
+ attributeListWriterJson.write(jGenerator, descriptor);
+ }
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(ATTRIBUTE_LIST_JSON, StandardCharsets.UTF_8);
+
+ JsonAssert.assertJsonEquals(expected, actual, Configuration.empty().withOptions(Option.IGNORING_EXTRA_FIELDS));
+ }
+
+ @Test
+ void writeEmptyAttributeListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ //when
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ jGenerator.writeStartObject();
+ try (jGenerator) {
+ attributeListWriterJson.write(jGenerator, null);
+ }
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(ATTRIBUTE_LIST_EMPTY_JSON, StandardCharsets.UTF_8);
+ JsonAssert.assertJsonEquals(expected, actual, Configuration.empty().withOptions(Option.IGNORING_EXTRA_FIELDS));
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CategorisationWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CategorisationWriterTest.java
new file mode 100644
index 0000000..ae6cf49
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CategorisationWriterTest.java
@@ -0,0 +1,45 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.Categorisation;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+
+class CategorisationWriterTest extends JsonWriterTestBase {
+
+ @InjectMocks
+ public CategorisationWriter categorisationWriter;
+
+ @Test
+ void writeCategorisationToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ Categorisation categorisation = MaintainableArtifactsTestUtils.buildCategorisation();
+
+ //when
+ categorisationWriter.writeAndClose(createJsonGenerator(stream), categorisation);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ MaintainableArtifactsTestUtils.assertMaintainableArtefactsInJson(actual);
+ String expected = IOUtils.resourceToString(TestUtils.CATEGORISATION_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"categorisations\": [" + actual + "] } }",
+ SdmxBeans::getCategorisations
+ );
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CategorySchemeWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CategorySchemeWriterTest.java
new file mode 100644
index 0000000..cc8e028
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CategorySchemeWriterTest.java
@@ -0,0 +1,44 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.CategorySchemeImpl;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+
+class CategorySchemeWriterTest extends JsonWriterTestBase {
+
+ @InjectMocks
+ public CategorySchemeWriter categorySchemeWriter;
+
+ @Test
+ void writeCategorySchemeToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ CategorySchemeImpl categoryScheme = MaintainableArtifactsTestUtils.buildCategoryScheme();
+
+ //when
+ categorySchemeWriter.writeAndClose(createJsonGenerator(stream), categoryScheme);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ MaintainableArtifactsTestUtils.assertMaintainableArtefactsInJson(actual);
+ String expected = IOUtils.resourceToString(TestUtils.CATEGORY_SCHEMA_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"categorySchemes\": [" + actual + "] } }",
+ SdmxBeans::getCategorySchemes
+ );
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CodelistWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CodelistWriterTest.java
new file mode 100644
index 0000000..fc7a090
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/CodelistWriterTest.java
@@ -0,0 +1,43 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.Codelist;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+
+import net.javacrumbs.jsonunit.JsonAssert;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+
+class CodelistWriterTest extends JsonWriterTestBase {
+
+ @InjectMocks
+ public CodelistWriter codeListWriter;
+
+ @Test
+ void writeCodeListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ Codelist codelist = MaintainableArtifactsTestUtils.buildCodeList();
+
+ //when
+ codeListWriter.writeAndClose(createJsonGenerator(stream), codelist);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ MaintainableArtifactsTestUtils.assertMaintainableArtefactsInJson(actual);
+ String expected = IOUtils.resourceToString(TestUtils.CODE_LIST_JSON, StandardCharsets.UTF_8);
+
+ JsonAssert.assertJsonEquals(expected, actual);
+
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"codelists\": [" + actual + "] } }",
+ SdmxBeans::getCodelists
+ );
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/ConceptSchemeWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/ConceptSchemeWriterTest.java
new file mode 100644
index 0000000..ad2c412
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/ConceptSchemeWriterTest.java
@@ -0,0 +1,44 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.ConceptSchemeImpl;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+
+import net.javacrumbs.jsonunit.JsonAssert;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+
+class ConceptSchemeWriterTest extends JsonWriterTestBase {
+
+ @InjectMocks
+ public ConceptSchemeWriter conceptSchemeWriter;
+
+ @Test
+ void writeConceptSchemeToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ ConceptSchemeImpl conceptScheme = MaintainableArtifactsTestUtils.buildConceptScheme();
+
+ //when
+ conceptSchemeWriter.writeAndClose(createJsonGenerator(stream), conceptScheme);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(TestUtils.CONCEPT_SCHEMA_JSON, StandardCharsets.UTF_8);
+
+ JsonAssert.assertJsonEquals(expected, actual);
+
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"conceptSchemes\": [" + actual + "] } }",
+ SdmxBeans::getConceptSchemes
+ );
+ }
+
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/ContentConstraintWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/ContentConstraintWriterTest.java
new file mode 100644
index 0000000..1df5064
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/ContentConstraintWriterTest.java
@@ -0,0 +1,65 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.DataConstraintImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataConstraint;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+
+public class ContentConstraintWriterTest extends JsonWriterTestBase {
+
+ @InjectMocks
+ private ContentConstraintWriter contentConstraintWriter;
+
+ @Test
+ void writeDataConstraintToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ DataConstraintImpl dataConstraint = MaintainableArtifactsTestUtils.buildDataConstraint();
+
+ //when
+ contentConstraintWriter.writeAndClose(createJsonGenerator(stream), dataConstraint);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(TestUtils.DATA_CONSTRAINT_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"contentconstraint\": [" + actual + "] } }",
+ SdmxBeans::getContentConstraintBeans
+ );
+ }
+
+ @Test
+ void writeMetaDataConstraintToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ MetadataConstraint metadataConstraint = MaintainableArtifactsTestUtils.buildMetadataConstraint();
+
+ //when
+ contentConstraintWriter.writeAndClose(createJsonGenerator(stream), metadataConstraint);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(TestUtils.META_DATA_CONSTRAINT_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"contentconstraint\": [" + actual + "] } }",
+ SdmxBeans::getContentConstraintBeans
+ );
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DataStructureDefinitionWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DataStructureDefinitionWriterTest.java
new file mode 100644
index 0000000..1ac64d0
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DataStructureDefinitionWriterTest.java
@@ -0,0 +1,71 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static com.epam.jsdmx.json10.structure.TestUtils.DATA_STRUCTURE_DEFINITION_JSON;
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.DataStructureDefinition;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.serializer.common.TimeDimensionLocalRepresentationAdapter;
+import com.epam.jsdmx.serializer.sdmx21.DataStructure30To21ComponentAdapter;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Spy;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+
+class DataStructureDefinitionWriterTest extends JsonWriterTestBase {
+
+ @InjectMocks
+ public DataStructureDefinitionWriter dataStructureDefinitionWriter;
+ @Spy
+ private AttributeListWriter attributeListWriterSpy = MaintainableArtifactsTestUtils.getAttributeListWriter();
+ @Spy
+ private DimensionListWriter dimensionListWriterSpy = MaintainableArtifactsTestUtils.getDimensionListWriter();
+ @Spy
+ private MeasureListWriter measureListWriterSpy = MaintainableArtifactsTestUtils.getMeasureListWriter();
+ @Spy
+ private GroupDimensionListWriter groupDimensionListWriter = MaintainableArtifactsTestUtils.getGroupDimensionListWriter();
+ @Spy
+ private DataStructure30To21ComponentAdapter dsdRecomposerSpy = new DataStructure30To21ComponentAdapter() {
+ @Override
+ public DataStructureDefinition recompose(DataStructureDefinition dsd) {
+ return dsd;
+ }
+ };
+ @Spy
+ private TimeDimensionLocalRepresentationAdapter dsdAdapterSpy = new TimeDimensionLocalRepresentationAdapter() {
+ @Override
+ public DataStructureDefinition adapt(DataStructureDefinition dsd) {
+ return dsd;
+ }
+ };
+
+ @Test
+ void writeDataStructureDefinitionToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ DataStructureDefinition dataStructureDefinition = MaintainableArtifactsTestUtils.buildDataStructureDefinition();
+
+ //when
+ dataStructureDefinitionWriter.writeAndClose(createJsonGenerator(stream), dataStructureDefinition);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ System.out.println(actual);
+ //then
+ String expected = IOUtils.resourceToString(DATA_STRUCTURE_DEFINITION_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+ // commented out due to bug in sdmxsource with npe when initializing dsd with attachment group
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"dataStructures\": [" + actual + "] } }",
+ SdmxBeans::getDataStructures
+ );
+ }
+
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DataflowWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DataflowWriterTest.java
new file mode 100644
index 0000000..13bad98
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DataflowWriterTest.java
@@ -0,0 +1,53 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.AnnotationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Dataflow;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalUri;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.sdmxsource.sdmx.api.model.beans.SdmxBeans;
+
+class DataflowWriterTest extends JsonWriterTestBase {
+ @InjectMocks
+ public DataflowWriter dataFlowWriter;
+
+ @Test
+ void writeDataFlowToJson() throws IOException, URISyntaxException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ Dataflow dataflow = MaintainableArtifactsTestUtils.buildDataFlow();
+ AnnotationImpl annotation = (AnnotationImpl) dataflow.getAnnotations()
+ .get(0);
+ annotation.setUrl(new InternationalUri(new URI("http://some.com")));
+ dataflow.getAnnotations()
+ .clear();
+ dataflow.getAnnotations()
+ .add(annotation);
+
+ //when
+ dataFlowWriter.writeAndClose(createJsonGenerator(stream), dataflow);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(TestUtils.DATA_FLOW_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+ sdmxSourceCompatibilityTester.test(
+ "{ \"meta\": {}, \"data\": { \"dataflows\": [" + actual + "] } }",
+ SdmxBeans::getDataflows
+ );
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DimensionListWriterJsonTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DimensionListWriterJsonTest.java
new file mode 100644
index 0000000..1e4669a
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/DimensionListWriterJsonTest.java
@@ -0,0 +1,90 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.DimensionDescriptorImpl;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import net.javacrumbs.jsonunit.JsonAssert;
+import net.javacrumbs.jsonunit.core.Configuration;
+import net.javacrumbs.jsonunit.core.Option;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+class DimensionListWriterJsonTest {
+
+ public static final String DIMENSION_LIST_JSON = "/serialization.expected/dimension-list.json";
+ public static final String DIMENSION_LIST_EMPTY_JSON = "/serialization.expected/dimension-list_empty.json";
+
+ @InjectMocks
+ public DimensionListWriter dimensionListWriterJson;
+ @Spy
+ private IdentifiableWriter annotableWriterSpy = MaintainableArtifactsTestUtils.getIdentifiableWriter();
+ @Spy
+ private ComponentWriter componentWriterSpy = MaintainableArtifactsTestUtils.getComponentWriter();
+ @Spy
+ private ConceptRoleWriter conceptRoleWriterSpy = MaintainableArtifactsTestUtils.getConceptRoleWriter();
+
+ AutoCloseable closeable;
+
+ @BeforeEach
+ void setup() {
+ closeable = MockitoAnnotations.openMocks(this);
+ }
+
+ @AfterEach
+ void teardown() throws Exception {
+ closeable.close();
+ }
+
+ @Test
+ void writeDimensionListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ DimensionDescriptorImpl descriptor = MaintainableArtifactsTestUtils.fillDimensionDescriptor();
+ //when
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ jGenerator.writeStartObject();
+ try (jGenerator) {
+ dimensionListWriterJson.write(jGenerator, descriptor);
+ }
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(DIMENSION_LIST_JSON, StandardCharsets.UTF_8);
+ System.out.println(expected);
+ JsonAssert.assertJsonEquals(expected, actual, Configuration.empty().withOptions(Option.IGNORING_EXTRA_FIELDS));
+ }
+
+ @Test
+ void writeEmptyDimensionListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ //when
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ jGenerator.writeStartObject();
+ try (jGenerator) {
+ dimensionListWriterJson.write(jGenerator, null);
+ }
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(DIMENSION_LIST_EMPTY_JSON, StandardCharsets.UTF_8);
+ System.out.println(expected);
+ JsonAssert.assertJsonEquals(expected, actual, Configuration.empty().withOptions(Option.IGNORING_EXTRA_FIELDS));
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/GenericTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/GenericTest.java
new file mode 100644
index 0000000..eba5689
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/GenericTest.java
@@ -0,0 +1,53 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.stream.Stream;
+
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactImpl;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class GenericTest extends JsonWriterTestBase {
+ @ParameterizedTest
+ @MethodSource("testArtefactWriters")
+ void writeToJson(String path, MaintainableWriter writer, T artefact) throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+ //when
+ writer.writeAndClose(createJsonGenerator(stream), artefact);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(path, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+ }
+
+ private static Stream testArtefactWriters() {
+
+ ReferenceAdapter referenceAdapter = MaintainableArtifactsTestUtils.getReferenceAdapter();
+ LinksWriter linksWriter = new LinksWriter(referenceAdapter);
+ AnnotableWriter annotableWriter = new AnnotableWriter(linksWriter);
+ IdentifiableWriter identifiableWriter = new IdentifiableWriter(annotableWriter);
+ NameableWriter nameableWriter = new NameableWriter(identifiableWriter);
+ VersionableWriter versionableWriter = new VersionableWriter(nameableWriter);
+ ContactsWriter contactsWriter = new ContactsWriter();
+ OrganisationWriter organisationWriter = new OrganisationWriter(contactsWriter, nameableWriter);
+ AgencySchemeWriter agencySchemeWriter = new AgencySchemeWriter(versionableWriter, linksWriter, organisationWriter);
+
+ return Stream.of(
+ Arguments.of(TestUtils.AGENCY_SCHEME_JSON, agencySchemeWriter, MaintainableArtifactsTestUtils.buildAgencyScheme())
+ );
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/GroupDimensionListWriterJsonTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/GroupDimensionListWriterJsonTest.java
new file mode 100644
index 0000000..a5322e0
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/GroupDimensionListWriterJsonTest.java
@@ -0,0 +1,59 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.epam.jsdmx.infomodel.sdmx30.GroupDimensionDescriptor;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import net.javacrumbs.jsonunit.JsonAssert;
+import net.javacrumbs.jsonunit.core.Configuration;
+import net.javacrumbs.jsonunit.core.Option;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+class GroupDimensionListWriterJsonTest {
+ public static final String GROUP_DIMENSION_LIST_JSON = "/serialization.expected/group-dimension-list.json";
+
+ @InjectMocks
+ public GroupDimensionListWriter groupDimensionListWriter;
+ @Spy
+ private IdentifiableWriter identifiableWriter = MaintainableArtifactsTestUtils.getIdentifiableWriter();
+
+ @BeforeEach
+ void setup() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void writeDimensionListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ List descriptors = MaintainableArtifactsTestUtils.fillListGroupDimensionDescriptor().stream()
+ .map(a -> (GroupDimensionDescriptor) a)
+ .collect(Collectors.toList());
+ //when
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ jGenerator.writeStartObject();
+ try (jGenerator) {
+ groupDimensionListWriter.write(jGenerator, descriptors);
+ }
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(GROUP_DIMENSION_LIST_JSON, StandardCharsets.UTF_8);
+
+ JsonAssert.assertJsonEquals(expected, actual, Configuration.empty().withOptions(Option.IGNORING_EXTRA_FIELDS));
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/HierarchicalCodelistWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/HierarchicalCodelistWriterTest.java
new file mode 100644
index 0000000..486779f
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/HierarchicalCodelistWriterTest.java
@@ -0,0 +1,67 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.Hierarchy;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.TestUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+
+class HierarchicalCodelistWriterTest extends JsonWriterTestBase {
+
+ @InjectMocks
+ public HierarchicalCodelistWriter hierarchyWriter;
+
+ @Test
+ void writeHierarchyToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ Hierarchy hierarchy = MaintainableArtifactsTestUtils.buildHierarchy();
+
+ //when
+ hierarchyWriter.writeAndClose(createJsonGenerator(stream), hierarchy);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(TestUtils.HIERARCHY_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+ // sdmx source has incorrect expectation about hierarchical codes field name:
+ // 'codes' VS 'hierarchicalCodes' as per specification
+
+// sdmxSourceCompatibilityTester.test(
+// "{ \"meta\": {}, \"data\": { \"hierarchicalCodelists\": [" + actual + "] } }",
+// SdmxBeans::getHierarchicalCodelists
+// );
+ }
+
+ @Test
+ void writeHierarchyWithInnerHierarchyToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ Hierarchy hierarchy = MaintainableArtifactsTestUtils.buildHierarchyWithInnerHierarchy();
+
+ //when
+ hierarchyWriter.writeAndClose(createJsonGenerator(stream), hierarchy);
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(TestUtils.HIERARCHY_WITH_CHILDREN_JSON, StandardCharsets.UTF_8);
+
+ assertJsonEquals(expected, actual);
+
+// sdmxSourceCompatibilityTester.test(
+// "{ \"meta\": {}, \"data\": { \"hierarchicalCodelists\": [" + actual + "] } }",
+// SdmxBeans::getHierarchicalCodelists
+// );
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/Json10StructureWriterTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/Json10StructureWriterTest.java
new file mode 100644
index 0000000..61bf555
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/Json10StructureWriterTest.java
@@ -0,0 +1,210 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static com.epam.jsdmx.json10.structure.writer.StructureUtils.SCHEMA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactsImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CategorisationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CategorySchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptScheme;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptSchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataStructureDefinitionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataflowImpl;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefact;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalString;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataStructureDefinitionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataflowImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Party;
+import com.epam.jsdmx.infomodel.sdmx30.RepresentationMapImpl;
+import com.epam.jsdmx.infomodel.sdmx30.StructureClassImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Version;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.serializer.sdmx30.common.Header;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.SneakyThrows;
+import net.javacrumbs.jsonunit.JsonAssert;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class Json10StructureWriterTest {
+
+ public static final String STRUCTURE_JSON = "/serialization.expected/structure.json";
+ public static final String EMPTY_STRUCTURE_JSON = "/serialization.expected/empty-structure.json";
+ private final ObjectMapper objectMapper = new ObjectMapper();
+ private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ private final DefaultHeaderProvider defaultHeaderProvider = mock(DefaultHeaderProvider.class);
+ private Json10StructureWriter jsonStructureWriter;
+
+ @BeforeEach
+ @SneakyThrows
+ void setup() {
+ jsonStructureWriter = MaintainableArtifactsTestUtils.createStructureWriter(
+ outputStream, objectMapper, defaultHeaderProvider);
+ when(defaultHeaderProvider.provide()).thenReturn(buildHeader());
+ }
+
+ @SneakyThrows
+ private Header buildHeader() {
+ Header header = new Header();
+ header.setSchema(new URI(SCHEMA));
+ header.setId("IDREF7553");
+ header.setContentLanguages(List.of(Locale.ENGLISH));
+ header.setTest(false);
+ header.setPrepared(Instant.parse("2022-04-03T15:35:01Z"));
+ header.setSender(buildParty());
+ return header;
+ }
+
+ private Party buildParty() {
+ Party party = new Party();
+ party.setId("unknown");
+ return party;
+ }
+
+ @Test
+ void writeArtefactsToJson() throws IOException {
+ ArtefactsImpl artefacts = new ArtefactsImpl();
+ fillCategorisation(artefacts);
+ fillCategoryScheme(artefacts);
+ fillConceptScheme(artefacts);
+ fillDataFlow(artefacts);
+ fillCodeList(artefacts);
+ fillDSD(artefacts);
+ fillHierarchy(artefacts);
+ fillMetadataFlow(artefacts);
+ fillMDSD(artefacts);
+ fillRepresentationMap(artefacts);
+ jsonStructureWriter.write(artefacts);
+ String actual = outputStream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(STRUCTURE_JSON, StandardCharsets.UTF_8);
+ JsonAssert.assertJsonEquals(expected, actual, JsonAssert.whenIgnoringPaths("meta"));
+ }
+
+ @Test
+ void writeEmptyArtefactsToJson() throws IOException {
+
+ ArtefactsImpl artefacts = new ArtefactsImpl();
+ jsonStructureWriter.write(artefacts);
+ String actual = outputStream.toString(StandardCharsets.UTF_8);
+
+ //then
+ String expected = IOUtils.resourceToString(EMPTY_STRUCTURE_JSON, StandardCharsets.UTF_8);
+ System.out.println(actual);
+ System.out.println(expected);
+ JsonAssert.assertJsonEquals(expected, actual, JsonAssert.whenIgnoringPaths("meta"));
+ }
+
+ private void fillCategoryScheme(ArtefactsImpl artefacts) {
+ CategorySchemeImpl categoryScheme = new CategorySchemeImpl();
+ categoryScheme.setId("CS1");
+ categoryScheme.setVersion(Version.createFromString("1.0"));
+ artefacts.setCategorySchemes(Set.of(categoryScheme));
+ }
+
+ private void fillMDSD(ArtefactsImpl artefacts) {
+ MetadataStructureDefinitionImpl metadataStructureDefinition = new MetadataStructureDefinitionImpl();
+ metadataStructureDefinition.setId("MDSD1");
+ metadataStructureDefinition.setVersion(Version.createFromString("1.0"));
+ artefacts.setMetadataStructureDefinitions(Set.of(metadataStructureDefinition));
+ }
+
+ private void fillDataFlow(ArtefactsImpl artefacts) {
+ DataflowImpl dataflow = new DataflowImpl();
+ dataflow.setVersion(Version.createFromString("1.0"));
+ dataflow.setId("DF1");
+ artefacts.setDataflows(Set.of(dataflow));
+ }
+
+ private void fillConceptScheme(ArtefactsImpl artefacts) {
+ ConceptSchemeImpl conceptScheme = new ConceptSchemeImpl();
+ conceptScheme.setId("CS1");
+ conceptScheme.setVersion(Version.createFromString("1.0"));
+ ConceptSchemeImpl conceptSchemeSec = new ConceptSchemeImpl();
+ conceptSchemeSec.setId("CS2");
+ conceptSchemeSec.setVersion(Version.createFromString("1.0"));
+ Set sortedSet = new TreeSet<>(Comparator.comparing(IdentifiableArtefact::getId));
+ sortedSet.add(conceptScheme);
+ sortedSet.add(conceptSchemeSec);
+ artefacts.setConceptSchemes(sortedSet);
+ }
+
+ private void fillCategorisation(ArtefactsImpl artefacts) {
+ CategorisationImpl categorisation = new CategorisationImpl();
+ IdentifiableArtefactReferenceImpl structureItemReference = new IdentifiableArtefactReferenceImpl(
+ "ARTEFACT",
+ "IMF",
+ "1.2",
+ StructureClassImpl.CATEGORY,
+ "CAT_TARGET"
+ );
+
+ categorisation.setCategorizedBy(structureItemReference);
+ IdentifiableArtefactReferenceImpl structureReference = new IdentifiableArtefactReferenceImpl(
+ "ARTEFACT",
+ "IMF",
+ "1.2",
+ StructureClassImpl.CATEGORISATION,
+ "CAT_SOURCE"
+ );
+ categorisation.setCategorizedArtefact(structureReference);
+ categorisation.setId("catid");
+ categorisation.setVersion(Version.createFromString("1.0"));
+ categorisation.setDescription(new InternationalString("categ"));
+ categorisation.setOrganizationId("EPM");
+ artefacts.setCategorisations(Set.of(categorisation));
+ }
+
+ private void fillCodeList(ArtefactsImpl artefacts) {
+ CodelistImpl codeList = new CodelistImpl();
+ codeList.setId("CL1");
+ codeList.setVersion(Version.createFromString("1.0"));
+ artefacts.setCodelists(Set.of(codeList));
+ }
+
+ private void fillDSD(ArtefactsImpl artefacts) {
+ DataStructureDefinitionImpl dataStructureDefinition = new DataStructureDefinitionImpl();
+ dataStructureDefinition.setId("DSD1");
+ dataStructureDefinition.setVersion(Version.createFromString("1.0"));
+ artefacts.setDataStructures(Set.of(dataStructureDefinition));
+ }
+
+ private void fillHierarchy(ArtefactsImpl artefacts) {
+ HierarchyImpl hierarchy = new HierarchyImpl();
+ hierarchy.setId("Hi1");
+ hierarchy.setVersion(Version.createFromString("1.0"));
+ artefacts.setHierarchies(Set.of(hierarchy));
+ }
+
+ private void fillMetadataFlow(ArtefactsImpl artefacts) {
+ MetadataflowImpl metadataflow = new MetadataflowImpl();
+ metadataflow.setId("MF1");
+ metadataflow.setVersion(Version.createFromString("1.0"));
+ artefacts.setMetadataflows(Set.of(metadataflow));
+ }
+
+ private void fillRepresentationMap(ArtefactsImpl artefacts) {
+ RepresentationMapImpl representationMap = new RepresentationMapImpl();
+ representationMap.setId("RM1");
+ representationMap.setVersion(Version.createFromString("1.0"));
+ artefacts.setRepresentationMaps(Set.of(representationMap));
+ }
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/JsonWriterTestBase.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/JsonWriterTestBase.java
new file mode 100644
index 0000000..91f2803
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/JsonWriterTestBase.java
@@ -0,0 +1,68 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+import com.epam.jsdmx.json10.structure.SdmxSourceCompatibilityTester;
+import com.epam.jsdmx.serializer.common.ReferenceAdapter;
+import com.epam.jsdmx.serializer.sdmx21.ReferenceResolver;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+class JsonWriterTestBase {
+
+ SdmxSourceCompatibilityTester sdmxSourceCompatibilityTester = new SdmxSourceCompatibilityTester();
+
+ @Spy
+ private VersionableWriter versionableWriterSpy = MaintainableArtifactsTestUtils.getVersionableWriter();
+ @Spy
+ private LinksWriter linksWriterSpy = MaintainableArtifactsTestUtils.getLinksWriter();
+ @Spy
+ private OrganisationWriter organisationWriter = MaintainableArtifactsTestUtils.getOrganisationWriter();
+ @Spy
+ private AnnotableWriter annotableWriter = MaintainableArtifactsTestUtils.getAnnotableWriter();
+ @Spy
+ private NameableWriter nameableWriter = MaintainableArtifactsTestUtils.getNameableWriter();
+ @Spy
+ private CodeWriterImpl codeWriter = MaintainableArtifactsTestUtils.getCodeImplWriter();
+ @Spy
+ private IdentifiableWriter identifiableWriter = MaintainableArtifactsTestUtils.getIdentifiableWriter();
+ @Spy
+ private RepresentationWriter representationWriter = MaintainableArtifactsTestUtils.getRepresentationWriter();
+ @Spy
+ private ReferenceResolver referenceResolver = MaintainableArtifactsTestUtils.getReferenceResolver();
+ @Spy
+ private ReferenceAdapter referenceAdapter = MaintainableArtifactsTestUtils.getReferenceAdapter();
+ @Spy
+ private DataKeySetsWriter dataKeySetsWriter = new DataKeySetsWriter();
+ @Spy
+ private CubeRegionWriter cubeRegionWriter = new CubeRegionWriter(new SelectionValueWriter(), annotableWriter);
+ @Spy
+ private MetadataConstraintWriter metadataConstraintWriter = new MetadataConstraintWriter(new SelectionValueWriter());
+
+ AutoCloseable closeable;
+
+ @BeforeEach
+ void setup() {
+ closeable = MockitoAnnotations.openMocks(this);
+ }
+
+ @AfterEach
+ void teardown() throws Exception {
+ closeable.close();
+ }
+
+ public JsonGenerator createJsonGenerator(ByteArrayOutputStream stream) throws IOException {
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ return jGenerator;
+ }
+
+}
diff --git a/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/MeasureListWriterJsonTest.java b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/MeasureListWriterJsonTest.java
new file mode 100644
index 0000000..8bec066
--- /dev/null
+++ b/sdmx-json10/src/test/java/com/epam/jsdmx/json10/structure/writer/MeasureListWriterJsonTest.java
@@ -0,0 +1,86 @@
+package com.epam.jsdmx.json10.structure.writer;
+
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import com.epam.jsdmx.infomodel.sdmx30.MeasureDescriptorImpl;
+import com.epam.jsdmx.json10.structure.MaintainableArtifactsTestUtils;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+class MeasureListWriterJsonTest {
+
+ public static final String MEASURE_LIST_JSON = "/serialization.expected/measure-list.json";
+ public static final String MEASURE_LIST_EMPTY_JSON = "/serialization.expected/measure-list_empty.json";
+ @InjectMocks
+ public MeasureListWriter measureListWriterJson;
+ @Spy
+ private IdentifiableWriter annotableWriterSpy = MaintainableArtifactsTestUtils.getIdentifiableWriter();
+ @Spy
+ private ComponentWriter componentWriterSpy = MaintainableArtifactsTestUtils.getComponentWriter();
+ @Spy
+ private ConceptRoleWriter conceptRoleWriterSpy = MaintainableArtifactsTestUtils.getConceptRoleWriter();
+
+ AutoCloseable closeable;
+
+ @BeforeEach
+ void setup() {
+ closeable = MockitoAnnotations.openMocks(this);
+ }
+
+ @AfterEach
+ void teardown() throws Exception {
+ closeable.close();
+ }
+
+ @Test
+ void writeMeasureListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ MeasureDescriptorImpl descriptor = MaintainableArtifactsTestUtils.fillMeasureDescriptor();
+ //when
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ jGenerator.writeStartObject();
+ try (jGenerator) {
+ measureListWriterJson.write(jGenerator, descriptor);
+ }
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(MEASURE_LIST_JSON, StandardCharsets.UTF_8);
+ assertJsonEquals(expected, actual);
+ }
+
+ @Test
+ void writeEmptyMeasureListToJson() throws IOException {
+ //given
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ //when
+ JsonFactory jfactory = new JsonFactory();
+ JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8);
+ jGenerator.writeStartObject();
+ try (jGenerator) {
+ measureListWriterJson.write(jGenerator, null);
+ }
+ String actual = stream.toString(StandardCharsets.UTF_8);
+ System.out.println(actual);
+
+ //then
+ String expected = IOUtils.resourceToString(MEASURE_LIST_EMPTY_JSON, StandardCharsets.UTF_8);
+ assertJsonEquals(expected, actual);
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/category-schema2.json b/sdmx-json10/src/test/resources/deserialization.expected/category-schema2.json
new file mode 100644
index 0000000..f4a0f85
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/category-schema2.json
@@ -0,0 +1,37 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2)",
+ "type": "categorisation",
+ "rel": "self"
+ }
+ ],
+ "isPartial": true,
+ "categories": []
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/codelist_wild.json b/sdmx-json10/src/test/resources/deserialization.expected/codelist_wild.json
new file mode 100644
index 0000000..33af17e
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/codelist_wild.json
@@ -0,0 +1,89 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:ARTEFACT(1.2)",
+ "type": "codelist",
+ "rel": "self"
+ }
+ ],
+ "isPartial": true,
+ "codes": [
+ {
+ "annotations": [],
+ "id": "CODE1",
+ "name": "code 1",
+ "names": {
+ "en": "code 1"
+ },
+ "description": "code 1 desc",
+ "descriptions": {
+ "en": "code 1 desc"
+ },
+ "parent": "CODE2"
+ },
+ {
+ "annotations": [],
+ "id": "CODE2",
+ "name": "code 2",
+ "names": {
+ "en": "code 2"
+ },
+ "description": "code 2 desc",
+ "descriptions": {
+ "en": "code 2 desc"
+ }
+ }
+ ],
+ "codelistExtensions": [
+ {
+ "prefix": "PREF2",
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "inclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "value": "mem1",
+ "cascadeSelection": "true"
+ }
+ ]
+ }
+ },
+ {
+ "prefix": "PREF2",
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "exclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "value": "mem2",
+ "cascadeSelection": "false"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/concept-schema.json b/sdmx-json10/src/test/resources/deserialization.expected/concept-schema.json
new file mode 100644
index 0000000..c563577
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/concept-schema.json
@@ -0,0 +1,66 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.conceptscheme.ConceptScheme=IMF:ARTEFACT(1.2)",
+ "type": "conceptscheme",
+ "rel": "self"
+ }
+ ],
+ "concepts": [
+ {
+ "annotations": [],
+ "id": "COUNTRY",
+ "name": "Country",
+ "names": {
+ "en": "Country"
+ },
+ "coreRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ },
+ {
+ "annotations": [],
+ "id": "COUNTRY2",
+ "name": "Country",
+ "names": {
+ "en": "Country"
+ },
+ "coreRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)",
+ "enumerationFormat": {
+ "pattern": "RYtGKbgicZaHCBRQDSx"
+ }
+ }
+ }
+ ],
+ "isPartial": true
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-flow.json b/sdmx-json10/src/test/resources/deserialization.expected/data-flow.json
new file mode 100644
index 0000000..429cb03
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-flow.json
@@ -0,0 +1,41 @@
+{
+ "agencyID": "IMF",
+ "id": "1",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ },
+ "links": [
+ {
+ "uri": "http://some.com"
+ }
+ ]
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=12:1(1.2)",
+ "type": "dataflow",
+ "rel": "self"
+ }
+ ],
+ "structure": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.2)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr.json
new file mode 100644
index 0000000..3bdfd15
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr.json
@@ -0,0 +1,94 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-1",
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-2",
+ "attributeRelationship": {
+ "dimensions": [
+ "dim1",
+ "dim2"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-3",
+ "attributeRelationship": {
+ "none": {}
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "annotations": [],
+ "id": "DA1",
+ "isMandatory": false,
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "group": "GroupKey"
+ },
+ "measureRelationship": [
+ "MO"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "dimensionList": {},
+ "measureList": {}
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr_and_null.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr_and_null.json
new file mode 100644
index 0000000..e4efb94
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr_and_null.json
@@ -0,0 +1,94 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-1",
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-2",
+ "attributeRelationship": {
+ "dimensions": [
+ "dim1",
+ "dim2"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-3",
+ "attributeRelationship": {
+ "none": {}
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "annotations": [],
+ "id": "DA1",
+ "isMandatory": false,
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "group": "GroupKey"
+ },
+ "measureRelationship": [
+ "MO"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "dimensionList": null,
+ "measureList": null
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr_null.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr_null.json
new file mode 100644
index 0000000..03b4110
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-attr_null.json
@@ -0,0 +1,94 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": null,
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "annotations": [],
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "annotations": [],
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod"
+ }
+ }
+ }
+ ]
+ },
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "MES1",
+ "isMandatory": false,
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-components_empty.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-components_empty.json
new file mode 100644
index 0000000..ce206b4
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-components_empty.json
@@ -0,0 +1,41 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {},
+ "dimensionList": {},
+ "measureList": {}
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-components_null.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-components_null.json
new file mode 100644
index 0000000..def0ab3
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-components_null.json
@@ -0,0 +1,41 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": null,
+ "dimensionList": null,
+ "measureList": null
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim.json
new file mode 100644
index 0000000..7df7d28
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim.json
@@ -0,0 +1,73 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {},
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "annotations": [],
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "annotations": [],
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod"
+ }
+ }
+ }
+ ]
+ },
+ "measureList": {}
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim_and_null.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim_and_null.json
new file mode 100644
index 0000000..ff0a5d9
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim_and_null.json
@@ -0,0 +1,73 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": null,
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "annotations": [],
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "annotations": [],
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod"
+ }
+ }
+ }
+ ]
+ },
+ "measureList": null
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim_null.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim_null.json
new file mode 100644
index 0000000..18d1555
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-dim_null.json
@@ -0,0 +1,115 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-1",
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-2",
+ "attributeRelationship": {
+ "dimensions": [
+ "dim1",
+ "dim2"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-3",
+ "attributeRelationship": {
+ "none": {}
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "annotations": [],
+ "id": "DA1",
+ "isMandatory": false,
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "group": "GroupKey"
+ },
+ "measureRelationship": [
+ "MO"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "dimensionList": null,
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "MES1",
+ "isMandatory": true,
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-full.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-full.json
new file mode 100644
index 0000000..3190f8b
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-full.json
@@ -0,0 +1,147 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-1",
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-2",
+ "attributeRelationship": {
+ "dimensions": [
+ "dim1",
+ "dim2"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-3",
+ "attributeRelationship": {
+ "none": {}
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "annotations": [],
+ "id": "DA1",
+ "isMandatory": false,
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "group": "GroupKey"
+ },
+ "measureRelationship": [
+ "MO"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "annotations": [],
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "annotations": [],
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod"
+ }
+ }
+ }
+ ]
+ },
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "MES1",
+ "isMandatory": false,
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas.json
new file mode 100644
index 0000000..42c4f99
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas.json
@@ -0,0 +1,62 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {},
+ "dimensionList": {},
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "MES1",
+ "isMandatory": true,
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas_and_null.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas_and_null.json
new file mode 100644
index 0000000..9f0fc37
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas_and_null.json
@@ -0,0 +1,62 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": null,
+ "dimensionList": null,
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "MES1",
+ "isMandatory": true,
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas_null.json b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas_null.json
new file mode 100644
index 0000000..7051230
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/data-structure-definition-meas_null.json
@@ -0,0 +1,126 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-1",
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-2",
+ "attributeRelationship": {
+ "dimensions": [
+ "dim1",
+ "dim2"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-3",
+ "attributeRelationship": {
+ "none": {}
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "annotations": [],
+ "id": "DA1",
+ "isMandatory": false,
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "group": "GroupKey"
+ },
+ "measureRelationship": [
+ "MO"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "annotations": [],
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "annotations": [],
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod"
+ }
+ }
+ }
+ ]
+ },
+ "measureList": null
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/hierarchy.json b/sdmx-json10/src/test/resources/deserialization.expected/hierarchy.json
new file mode 100644
index 0000000..88c9a40
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/hierarchy.json
@@ -0,0 +1,88 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Hierarchy=IMF:ARTEFACT(1.2)",
+ "type": "hierarchy",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "hasFormalLevels": true,
+ "hierarchicalCodes": [
+ {
+ "annotations": [
+ {
+ "id": "id",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "id": "id",
+ "validFrom": "2022-07-17T10:11:16.345Z",
+ "validTo": "2022-07-17T10:11:16.345Z",
+ "code": "urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1",
+ "level": "Lev1",
+ "hierarchicalCodes": [
+ {
+ "annotations": [],
+ "id": "1",
+ "validFrom": "2022-07-17T10:11:16.345Z",
+ "validTo": "2022-07-17T10:11:16.345Z",
+ "code": "urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1",
+ "level": null
+ }
+ ]
+ }
+ ],
+ "level": {
+ "annotations": [],
+ "id": "levId",
+ "name": "hierarchy name",
+ "names": {
+ "en": "hierarchy name"
+ },
+ "description": "hierarchy desc",
+ "descriptions": {
+ "en": "hierarchy desc"
+ },
+ "codingFormat": {
+ "interval": 1
+ },
+ "level": {
+ "annotations": [],
+ "id": "levUn",
+ "name": "underLevel",
+ "names": {
+ "en": "underLevel"
+ },
+ "codingFormat": {}
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/json.json b/sdmx-json10/src/test/resources/deserialization.expected/json.json
new file mode 100644
index 0000000..cbacccb
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/json.json
@@ -0,0 +1,158 @@
+{
+ "data": {
+ "categorisations": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2)",
+ "type": "categorisation",
+ "rel": "self"
+ }
+ ],
+ "source": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2).CAT_SOURCE",
+ "target": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Category=IMF:ARTEFACT(1.2).CAT_TARGET"
+ }
+ ],
+ "structureMaps": [
+ {
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "id": "FR1",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "id": "FR2",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_3",
+ "datePattern": "2022-11",
+ "id": "FR3",
+ "annotations": []
+ }
+ ],
+ "datePatternMaps": [
+ {
+ "mappedComponents": [
+ {
+ "source": "CompMapDPS",
+ "target": "CompMapDPT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR1",
+ "FR2"
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "DP1",
+ "locale": "EN",
+ "yearStart": {},
+ "sourcePattern": "SOURCE"
+ }
+ ],
+ "fixedValueMaps": [
+ {
+ "annotations": [],
+ "value": "value"
+ }
+ ],
+ "epochMaps": [
+ {
+ "basePeriod": "2022-07-17T10:11:16.000000",
+ "mappedComponents": [
+ {
+ "source": "CompMapEpochS",
+ "target": "CompMapEpochT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR3"
+ ],
+ "epochPeriod": "Microsecond",
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "EPOCH1",
+ "yearStart": {}
+ }
+ ],
+ "componentMaps": [
+ {
+ "source": [
+ "CompMapRepS"
+ ],
+ "target": [
+ "CompMapRepT"
+ ],
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)"
+ }
+ ],
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=QUANTHUB:STRUC_ID(1.2)",
+ "rel": "self",
+ "type": "structuremap"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16.000000"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/metadata-all-artefacts.json b/sdmx-json10/src/test/resources/deserialization.expected/metadata-all-artefacts.json
new file mode 100644
index 0000000..59106b4
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/metadata-all-artefacts.json
@@ -0,0 +1,74 @@
+{
+ "data": {
+ "metadataSets": [
+ {
+ "metadataflow": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=EPM:MetadataSet(1.8)",
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "reportingEnd": "2022",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "targets": [
+ "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=EPM:MetadataSetTar(1.8)"
+ ],
+ "reportingBegin": "2022",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "publicationPeriod": "2022-03",
+ "publicationYear": "2022",
+ "action": "Append",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=12:1(1.2)",
+ "rel": "self",
+ "type": "metadataset"
+ }
+ ],
+ "attributes": [
+ {
+ "id": "attr1",
+ "value": "someVal",
+ "attributes": [
+ {
+ "id": "attr2",
+ "value": "someVal2"
+ }
+ ]
+ }
+ ],
+ "id": "1",
+ "validTo": "2022-07-17T10:11:16Z"
+ }
+ ]
+ },
+ "meta": {
+ "schema": "https://raw.githubusercontent.com/sdmx-twg/sdmx-json/master/metadata-message/tools/schemas/2.0.0/sdmx-json-metadata-schema.json",
+ "id": "IDREF8124",
+ "test": false,
+ "prepared": "2021-11-02T09:47:41.000000",
+ "contentLanguages": [
+ "en"
+ ],
+ "sender": {
+ "id": "unknown"
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/structure-maps-full.json b/sdmx-json10/src/test/resources/deserialization.expected/structure-maps-full.json
new file mode 100644
index 0000000..17e86dc
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/structure-maps-full.json
@@ -0,0 +1,119 @@
+{
+ "data": {
+ "structureMaps": [
+ {
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "id": "FR1",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "id": "FR2",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_3",
+ "datePattern": "2022-11",
+ "id": "FR3",
+ "annotations": []
+ }
+ ],
+ "datePatternMaps": [
+ {
+ "mappedComponents": [
+ {
+ "source": "CompMapDPS",
+ "target": "CompMapDPT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR1",
+ "FR2"
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "DP1",
+ "locale": "EN",
+ "yearStart": {},
+ "sourcePattern": "SOURCE"
+ }
+ ],
+ "fixedValueMaps": [
+ {
+ "annotations": [],
+ "value": "value"
+ }
+ ],
+ "epochMaps": [
+ {
+ "basePeriod": "2022-07-17T10:11:16.000000",
+ "mappedComponents": [
+ {
+ "source": "CompMapEpochS",
+ "target": "CompMapEpochT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR3"
+ ],
+ "epochPeriod": "Microsecond",
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "EPOCH1",
+ "yearStart": {}
+ }
+ ],
+ "componentMaps": [
+ {
+ "source": [
+ "CompMapRepS"
+ ],
+ "target": [
+ "CompMapRepT"
+ ],
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)"
+ }
+ ],
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "structuremap"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16.000000"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/structure-maps-list-full.json b/sdmx-json10/src/test/resources/deserialization.expected/structure-maps-list-full.json
new file mode 100644
index 0000000..55c0a9a
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/structure-maps-list-full.json
@@ -0,0 +1,201 @@
+{
+ "data": {
+ "structureMaps": [
+ {
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "id": "FR1",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "id": "FR2",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_3",
+ "datePattern": "2022-11",
+ "id": "FR3",
+ "annotations": []
+ }
+ ],
+ "datePatternMaps": [
+ {
+ "mappedComponents": [
+ {
+ "source": "CompMapDPS",
+ "target": "CompMapDPT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR1",
+ "FR2"
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "DP1",
+ "locale": "EN",
+ "yearStart": {},
+ "sourcePattern": "SOURCE"
+ }
+ ],
+ "fixedValueMaps": [
+ {
+ "annotations": [],
+ "value": "value"
+ }
+ ],
+ "epochMaps": [
+ {
+ "basePeriod": "2022-07-17T10:11:16.000000",
+ "mappedComponents": [
+ {
+ "source": "CompMapEpochS",
+ "target": "CompMapEpochT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR3"
+ ],
+ "epochPeriod": "Microsecond",
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "EPOCH1",
+ "yearStart": {}
+ }
+ ],
+ "componentMaps": [
+ {
+ "source": [
+ "CompMapRepS"
+ ],
+ "target": [
+ "CompMapRepT"
+ ],
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)"
+ }
+ ],
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "structuremap"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16.000000"
+ },
+ {
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "id": "FR1",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "id": "FR2",
+ "annotations": []
+ }
+ ],
+ "datePatternMaps": [
+ {
+ "mappedComponents": [
+ {
+ "source": "CompMapDPS",
+ "target": "CompMapDPT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR1",
+ "FR2"
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "DP1",
+ "locale": "EN",
+ "yearStart": {},
+ "sourcePattern": "SOURCE"
+ }
+ ],
+ "componentMaps": [
+ {
+ "source": [
+ "CompMapRepS"
+ ],
+ "target": [
+ "CompMapRepT"
+ ],
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=QUANTHUB:STRUC_ID2(1.2).REPRES"
+ }
+ ],
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "QUANTHUB",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=QUANTHUB:STRUC_ID2(1.2)",
+ "rel": "self",
+ "type": "structuremap"
+ }
+ ],
+ "id": "STRUC_ID2",
+ "validTo": "2022-07-17T10:11:16.000000"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/structures-all-artefacts.json b/sdmx-json10/src/test/resources/deserialization.expected/structures-all-artefacts.json
new file mode 100644
index 0000000..a495ae5
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/structures-all-artefacts.json
@@ -0,0 +1,811 @@
+{
+ "data": {
+ "categorisations": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2)",
+ "type": "categorisation",
+ "rel": "self"
+ }
+ ],
+ "source": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2).CAT_SOURCE",
+ "target": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Category=IMF:ARTEFACT(1.2).CAT_TARGET"
+ }
+ ],
+ "structureMaps": [
+ {
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "id": "FR1",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "id": "FR2",
+ "annotations": []
+ },
+ {
+ "frequencyId": "CODE_3",
+ "datePattern": "2022-11",
+ "id": "FR3",
+ "annotations": []
+ }
+ ],
+ "datePatternMaps": [
+ {
+ "mappedComponents": [
+ {
+ "source": "CompMapDPS",
+ "target": "CompMapDPT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR1",
+ "FR2"
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "DP1",
+ "locale": "EN",
+ "yearStart": {},
+ "sourcePattern": "SOURCE"
+ }
+ ],
+ "fixedValueMaps": [
+ {
+ "annotations": [],
+ "value": "value"
+ }
+ ],
+ "epochMaps": [
+ {
+ "basePeriod": "2022-07-17T10:11:16.000000",
+ "mappedComponents": [
+ {
+ "source": "CompMapEpochS",
+ "target": "CompMapEpochT"
+ }
+ ],
+ "mappedFrequencies": [
+ "FR3"
+ ],
+ "epochPeriod": "Microsecond",
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "EPOCH1",
+ "yearStart": {}
+ }
+ ],
+ "componentMaps": [
+ {
+ "source": [
+ "CompMapRepS"
+ ],
+ "target": [
+ "CompMapRepT"
+ ],
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)"
+ }
+ ],
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "structuremap"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16.000000"
+ }
+ ],
+ "categorySchemes": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.CategoryScheme=IMF:ARTEFACT(1.2)",
+ "type": "categoryscheme",
+ "rel": "self"
+ }
+ ],
+ "isPartial": true,
+ "categories": [
+ {
+ "annotations": [],
+ "id": "23423",
+ "name": "Category1",
+ "names": {
+ "en": "Category1"
+ },
+ "description": "description",
+ "descriptions": {
+ "en": "description"
+ },
+ "categories": [
+ {
+ "annotations": [],
+ "id": "23422",
+ "name": "Category2",
+ "names": {
+ "en": "Category2"
+ },
+ "description": "description category1",
+ "descriptions": {
+ "en": "description category1"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "codeLists": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:ARTEFACT(1.2)",
+ "type": "codelist",
+ "rel": "self"
+ }
+ ],
+ "isPartial": true,
+ "codes": [
+ {
+ "annotations": [],
+ "id": "CODE1",
+ "name": "code 1",
+ "names": {
+ "en": "code 1"
+ },
+ "description": "code 1 desc",
+ "descriptions": {
+ "en": "code 1 desc"
+ },
+ "parent": "CODE2"
+ },
+ {
+ "annotations": [],
+ "id": "CODE2",
+ "name": "code 2",
+ "names": {
+ "en": "code 2"
+ },
+ "description": "code 2 desc",
+ "descriptions": {
+ "en": "code 2 desc"
+ }
+ }
+ ],
+ "codelistExtensions": [
+ {
+ "prefix": "PREF2",
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "inclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "value": "mem1",
+ "cascadeSelection": "true"
+ }
+ ]
+ }
+ },
+ {
+ "prefix": "PREF2",
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "exclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "value": "mem2",
+ "cascadeSelection": "false"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ],
+ "conceptSchemes": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.conceptscheme.ConceptScheme=IMF:ARTEFACT(1.2)",
+ "type": "conceptscheme",
+ "rel": "self"
+ }
+ ],
+ "concepts": [
+ {
+ "annotations": [],
+ "id": "COUNTRY",
+ "name": "Country",
+ "names": {
+ "en": "Country"
+ },
+ "coreRepresentation": {}
+ }
+ ]
+ }
+ ],
+ "dataStructures": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=12:1(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [
+ {
+ "annotations": [],
+ "metadataAttributeReference": "12",
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "1",
+ "attributeRelationship": {
+ "dimensions": [
+ "dim1",
+ "dim2"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "2",
+ "attributeRelationship": {
+ "none": {}
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "annotations": [],
+ "id": "DA1",
+ "isMandatory": true,
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "none": {}
+ },
+ "measureRelationship": [
+ "MO"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "annotations": [],
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "annotations": [],
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {}
+ }
+ ]
+ },
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "MES1",
+ "isMandatory": true,
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "hierarchies": [
+ {
+ "level": {
+ "names": {
+ "en": "hierarchy name"
+ },
+ "name": "hierarchy name",
+ "annotations": [],
+ "description": "hierarchy desc",
+ "id": "levId",
+ "descriptions": {
+ "en": "hierarchy desc"
+ },
+ "codingFormat": {
+ "interval": 1,
+ "isSequence": true,
+ "maxLength": 1,
+ "minLength": 1,
+ "maxValue": 1,
+ "minValue": 1,
+ "pattern": "a",
+ "startValue": 1,
+ "endValue": 1,
+ "dataType": "Alpha"
+ }
+ },
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "hierarchicalCodes": [
+ {
+ "code": "urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1",
+ "level": "Lev1",
+ "annotations": [],
+ "validFrom": "2022-07-17T10:11:16.345Z",
+ "validTo": "2022-07-17T10:11:16.345Z"
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Hierarchy=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "hierarchy"
+ }
+ ],
+ "id": "ARTEFACT",
+ "hasFormalLevels": true,
+ "validTo": "2022-07-17T10:11:16.000000"
+ }
+ ],
+ "metadataflows": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=IMF:ARTEFACT(1.2)",
+ "type": "metadataflow",
+ "rel": "self"
+ }
+ ],
+ "structure": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=IMF:METADATA(1.0.0)"
+ }
+ ],
+ "metadataStructures": [
+ {
+ "metadataStructureComponents": {
+ "metadataAttributeList": {
+ "annotations": [
+ {
+ "id": "Annot-1",
+ "text": null,
+ "title": null,
+ "type": null,
+ "value": null
+ }
+ ],
+ "metadataAttributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttributeDescriptor=1:1(1.0).2",
+ "minOccurs": 10,
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=EPM:dim1(1.0)"
+ },
+ "annotations": [],
+ "maxOccurs": 20,
+ "id": "1",
+ "isPresentational": true,
+ "metadataAttributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttributeDescriptor=1:1(1.0).2",
+ "minOccurs": 10,
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=EPM:dim1(1.0)"
+ },
+ "annotations": [],
+ "maxOccurs": 20,
+ "id": "1",
+ "isPresentational": true
+ }
+ ]
+ }
+ ],
+ "id": "1"
+ }
+ },
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "description": "Some artefact",
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "agencyID": "IMF",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "metadatastructure"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "version": "1.2",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000"
+ }
+ ],
+ "representationMaps": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)",
+ "type": "representationmap",
+ "rel": "self"
+ }
+ ],
+ "source": [
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:SOURCE(1.5.1)"
+ },
+ {
+ "dataType": "DateTime"
+ }
+ ],
+ "target": [
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST_SOURCE(1.0)"
+ }
+ ],
+ "representationMappings": [
+ {
+ "annotations": [],
+ "validTo": "2022-07-17T00:00:00.000Z",
+ "validFrom": "2022-07-17T00:00:00.000Z",
+ "sourceValues": [
+ {
+ "value": "mappedValue",
+ "isRegEx": true,
+ "startIndex": 0,
+ "endIndex": 10
+ }
+ ],
+ "targetValues": [
+ "targetValue1",
+ "targetValue2"
+ ]
+ }
+ ]
+ }
+ ],
+ "dataflows": [
+ {
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=IMF:ARTEFACT(1.2)",
+ "type": "dataflow",
+ "rel": "self"
+ }
+ ],
+ "structure": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.2)"
+ }
+ ]
+ },
+ "meta": {
+ "schema": "https://raw.githubusercontent.com/sdmx-twg/sdmx-json/master/metadata-message/tools/schemas/2.0.0/sdmx-json-metadata-schema.json",
+ "id": "IDREF8124",
+ "test": false,
+ "prepared": "2021-11-02T09:47:41.000000",
+ "contentLanguages": [
+ "en"
+ ],
+ "sender": {
+ "id": "unknown"
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/deserialization.expected/test.json b/sdmx-json10/src/test/resources/deserialization.expected/test.json
new file mode 100644
index 0000000..135d5a7
--- /dev/null
+++ b/sdmx-json10/src/test/resources/deserialization.expected/test.json
@@ -0,0 +1,147 @@
+{
+ "dataStructureComponents": {
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [],
+ "attributes": [
+ {
+ "annotations": [],
+ "id": "COMMENT",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=QUANTHUB:ECOFIN_CONCEPTS(1.0).COMMENT",
+ "isMandatory": false,
+ "conceptRoles": [],
+ "attributeRelationship": {
+ "none": {}
+ }
+ },
+ {
+ "annotations": [],
+ "id": "BASE_PER",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=SDMX:CROSS_DOMAIN_CONCEPTS(1.0).BASE_PER",
+ "isMandatory": false,
+ "conceptRoles": [],
+ "attributeRelationship": {
+ "dimensions": [
+ "DATA_DOMAIN",
+ "REF_AREA",
+ "INDICATOR",
+ "COUNTERPART_AREA",
+ "FREQ"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "id": "UNIT_MULT",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=SDMX:CROSS_DOMAIN_CONCEPTS(1.0).UNIT_MULT",
+ "isMandatory": false,
+ "conceptRoles": [],
+ "attributeRelationship": {
+ "dimensions": [
+ "DATA_DOMAIN",
+ "REF_AREA",
+ "INDICATOR",
+ "COUNTERPART_AREA",
+ "FREQ"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "id": "OBS_STATUS",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=SDMX:CROSS_DOMAIN_CONCEPTS(1.0).OBS_STATUS",
+ "isMandatory": false,
+ "conceptRoles": [],
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "id": "TIME_FORMAT",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=SDMX:CROSS_DOMAIN_CONCEPTS(1.0).TIME_FORMAT",
+ "isMandatory": false,
+ "conceptRoles": [],
+ "attributeRelationship": {
+ "dimensions": [
+ "DATA_DOMAIN",
+ "REF_AREA",
+ "INDICATOR",
+ "COUNTERPART_AREA",
+ "FREQ"
+ ]
+ }
+ }
+ ]
+ },
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "annotations": [],
+ "id": "DATA_DOMAIN",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=QUANTHUB:ECOFIN_CONCEPTS(1.0).DATA_DOMAIN",
+ "position": 0,
+ "type": "Dimension",
+ "conceptRoles": []
+ },
+ {
+ "annotations": [],
+ "id": "REF_AREA",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=QUANTHUB:ECOFIN_CONCEPTS(2.0.0-draft).REF_AREA",
+ "position": 1,
+ "type": "Dimension",
+ "conceptRoles": []
+ },
+ {
+ "annotations": [],
+ "id": "INDICATOR",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=QUANTHUB:ECOFIN_CONCEPTS(1.0).INDICATOR",
+ "position": 2,
+ "type": "Dimension",
+ "conceptRoles": []
+ },
+ {
+ "annotations": [],
+ "id": "COUNTERPART_AREA",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=QUANTHUB:ECOFIN_CONCEPTS(1.0).COUNTERPART_AREA",
+ "position": 3,
+ "type": "Dimension",
+ "conceptRoles": []
+ },
+ {
+ "annotations": [],
+ "id": "FREQ",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=SDMX:CROSS_DOMAIN_CONCEPTS(1.0).FREQ",
+ "position": 4,
+ "type": "Dimension",
+ "conceptRoles": []
+ }
+ ],
+ "timeDimensions": [
+ {
+ "annotations": [],
+ "id": "TIME_PERIOD",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=SDMX:CROSS_DOMAIN_CONCEPTS(1.0).TIME_PERIOD",
+ "position": 5,
+ "type": "TimeDimension"
+ }
+ ]
+ },
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "OBS_VALUE",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=SDMX:CROSS_DOMAIN_CONCEPTS(1.0).OBS_VALUE",
+ "isMandatory": false,
+ "conceptRoles": []
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/agency-scheme.json b/sdmx-json10/src/test/resources/serialization.expected/agency-scheme.json
new file mode 100644
index 0000000..2b8685a
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/agency-scheme.json
@@ -0,0 +1,148 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.base.AgencyScheme=SDMX:AGENCIES(1.0)",
+ "type": "agencyscheme",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "AGENCIES",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.0",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "SDMX",
+ "isPartial": false,
+ "isFinal": true,
+ "agencies": [
+ {
+ "annotations": [
+ {
+ "id": "Annot"
+ }
+ ],
+ "id": "AG-1",
+ "name": "Agency",
+ "names": {
+ "en": "Agency"
+ },
+ "description": "Agency",
+ "descriptions": {
+ "en": "Agency"
+ },
+ "contacts": [
+ {
+ "name": "AG_1",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ },
+ {
+ "name": "AG_2",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax2"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "AG-2",
+ "name": "Agency",
+ "names": {
+ "en": "Agency"
+ },
+ "description": "Agency",
+ "descriptions": {
+ "en": "Agency"
+ },
+ "contacts": [
+ {
+ "name": "AG_1",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ },
+ {
+ "name": "AG_2",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax2"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/attribute-list.json b/sdmx-json10/src/test/resources/serialization.expected/attribute-list.json
new file mode 100644
index 0000000..efaac68
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/attribute-list.json
@@ -0,0 +1,26 @@
+{
+ "attributeList": {
+ "id": "AttributeDescriptor",
+ "attributes": [
+ {
+ "id": "DA1",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "localRepresentation": {
+ "textFormat": {
+ "textType": "AlphaNumeric"
+ }
+ },
+ "assignmentStatus": "Conditional",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "dimensions": [
+ "DIM1"
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/attribute-list_empty.json b/sdmx-json10/src/test/resources/serialization.expected/attribute-list_empty.json
new file mode 100644
index 0000000..c618b3a
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/attribute-list_empty.json
@@ -0,0 +1,3 @@
+{
+ "attributeList": null
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/categorisation.json b/sdmx-json10/src/test/resources/serialization.expected/categorisation.json
new file mode 100644
index 0000000..0df21d1
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/categorisation.json
@@ -0,0 +1,36 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2)",
+ "type": "categorisation",
+ "rel": "self"
+ }
+ ],
+ "source": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2)",
+ "target": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Category=IMF:ARTEFACT(1.2).CAT_TARGET",
+ "isFinal": true
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/category-schema.json b/sdmx-json10/src/test/resources/serialization.expected/category-schema.json
new file mode 100644
index 0000000..b8fd488
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/category-schema.json
@@ -0,0 +1,92 @@
+{
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "type": "type1"
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "isPartial": true,
+ "isFinal": true,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.CategoryScheme=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "categoryscheme"
+ }
+ ],
+ "id": "ARTEFACT",
+ "categories": [
+ {
+ "names": {
+ "en": "Category1"
+ },
+ "name": "Category1",
+ "annotations": [
+ {
+ "id": "Annot"
+ }
+ ],
+ "description": "description category 1",
+ "id": "Category-1",
+ "categories": [
+ {
+ "names": {
+ "en": "Category2"
+ },
+ "name": "Category2",
+ "annotations": [
+ {
+ "id": "Annot"
+ },
+ {
+ "id": "Annot2"
+ }
+ ],
+ "description": "description category2",
+ "id": "Category-2",
+ "descriptions": {
+ "en": "description category2"
+ }
+ },
+ {
+ "names": {
+ "en": "Category3"
+ },
+ "name": "Category3",
+ "description": "description category3",
+ "id": "Category-3",
+ "descriptions": {
+ "en": "description category3"
+ }
+ }
+ ],
+ "descriptions": {
+ "en": "description category 1"
+ }
+ },
+ {
+ "names": {
+ "en": "Category4"
+ },
+ "name": "Category4",
+ "id": "Category-4"
+ }
+ ],
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/category-scheme-map.json b/sdmx-json10/src/test/resources/serialization.expected/category-scheme-map.json
new file mode 100644
index 0000000..174df14
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/category-scheme-map.json
@@ -0,0 +1,60 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.CategorySchemeMap=IMF:ARTEFACT(1.2)",
+ "type": "categoryschememap",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "isFinal": true,
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "itemMaps": [
+ {
+ "annotations": [
+ {
+ "id": "Ann"
+ }
+ ],
+ "sourceValue": "S-1",
+ "targetValue": "IM-1",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ },
+ {
+ "annotations": [
+ {
+ "id": "Ann"
+ }
+ ],
+ "sourceValue": "S-2",
+ "targetValue": "IM-2",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ }
+ ],
+ "source": "urn:sdmx:org.sdmx.infomodel.categoryscheme.CategoryScheme=IMF:CatScheme(1.0)",
+ "target": "urn:sdmx:org.sdmx.infomodel.categoryscheme.CategoryScheme=IMF:CatScheme(1.1)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/codelist.json b/sdmx-json10/src/test/resources/serialization.expected/codelist.json
new file mode 100644
index 0000000..897c17e
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/codelist.json
@@ -0,0 +1,60 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "isFinal": true,
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:ARTEFACT(1.2)",
+ "type": "codelist",
+ "rel": "self"
+ }
+ ],
+ "isPartial": true,
+ "codes": [
+ {
+ "id": "CODE1",
+ "name": "code 1",
+ "names": {
+ "en": "code 1"
+ },
+ "description": "code 1 desc",
+ "descriptions": {
+ "en": "code 1 desc"
+ },
+ "parent": "CODE2"
+ },
+ {
+ "id": "CODE2",
+ "name": "code 2",
+ "names": {
+ "en": "code 2"
+ },
+ "description": "code 2 desc",
+ "descriptions": {
+ "en": "code 2 desc"
+ }
+ }
+ ]
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/concept-scheme-map.json b/sdmx-json10/src/test/resources/serialization.expected/concept-scheme-map.json
new file mode 100644
index 0000000..facf609
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/concept-scheme-map.json
@@ -0,0 +1,60 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.ConceptSchemeMap=IMF:ARTEFACT(1.2)",
+ "type": "conceptschememap",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "isFinal": true,
+ "itemMaps": [
+ {
+ "annotations": [
+ {
+ "id": "Ann"
+ }
+ ],
+ "sourceValue": "S-1",
+ "targetValue": "IM-1",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ },
+ {
+ "annotations": [
+ {
+ "id": "Ann"
+ }
+ ],
+ "sourceValue": "S-2",
+ "targetValue": "IM-2",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ }
+ ],
+ "source": "urn:sdmx:org.sdmx.infomodel.conceptscheme.ConceptScheme=IMF:ConScheme(1.0)",
+ "target": "urn:sdmx:org.sdmx.infomodel.conceptscheme.ConceptScheme=IMF:ConScheme(1.1)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/concept-scheme.json b/sdmx-json10/src/test/resources/serialization.expected/concept-scheme.json
new file mode 100644
index 0000000..355bedf
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/concept-scheme.json
@@ -0,0 +1,49 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.conceptscheme.ConceptScheme=IMF:ARTEFACT(1.2)",
+ "type": "conceptscheme",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "concepts": [
+ {
+ "id": "COUNTRY",
+ "name": "Country",
+ "names": {
+ "en": "Country"
+ },
+ "coreRepresentation": {
+ "textFormat": {
+ "textType": "Alpha"
+ }
+ }
+ }
+ ],
+ "isPartial": true,
+ "isFinal": true
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/data-constraint.json b/sdmx-json10/src/test/resources/serialization.expected/data-constraint.json
new file mode 100644
index 0000000..deef951
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/data-constraint.json
@@ -0,0 +1,99 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.registry.DataConstraint=IMF:ARTEFACT(1.2)",
+ "type": "dataconstraint",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "isFinal": true,
+ "agencyID": "IMF",
+ "constraintAttachment": {
+ "dataProvider": "urn:sdmx:org.sdmx.infomodel.base.DataProvider=IMF:DATA_PROVIDERS(1.0).DP"
+ },
+ "role": "Allowed",
+ "releaseCalendar": {
+ "tolerance": "Tolerance",
+ "offset": "Offset",
+ "periodicity": "Periodicity"
+ },
+ "cubeRegions": [
+ {
+ "isIncluded": true,
+ "attributes": [
+ {
+ "id": "some component id 1",
+ "values": [
+ "selectionValue local",
+ "selectionValue1"
+ ]
+ },
+ {
+ "id": "some component id 2",
+ "values": [
+ "selectionValue2",
+ "selectionValue5"
+ ]
+ }
+ ],
+ "keyValues": [
+ {
+ "id": "123",
+ "timeRange": {
+ "beforePeriod": {
+ "period": "2020",
+ "isInclusive": false
+ },
+ "afterPeriod": {
+ "period": "2021",
+ "isInclusive": false
+ }
+ }
+ }
+ ],
+ "annotations": [
+ {
+ "id": "Annot"
+ }
+ ]
+ }
+ ],
+ "dataKeySets": [
+ {
+ "isIncluded": true,
+ "keys": [
+ {
+ "keyValues": [
+ {
+ "id": "C1",
+ "value": "Value"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/data-consumer-scheme.json b/sdmx-json10/src/test/resources/serialization.expected/data-consumer-scheme.json
new file mode 100644
index 0000000..b6e90e5
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/data-consumer-scheme.json
@@ -0,0 +1,90 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.base.DataConsumerScheme=IMF:DATA_CONSUMERS(1.0)",
+ "type": "dataconsumerscheme",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "DATA_CONSUMERS",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.0",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "isPartial": false,
+ "isFinal": true,
+ "dataConsumers": [
+ {
+ "id": "DC",
+ "name": "Data Consumer",
+ "names": {
+ "en": "Data Consumer"
+ },
+ "description": "Data Consumer description",
+ "descriptions": {
+ "en": "Data Consumer description"
+ },
+ "contacts": [
+ {
+ "name": "AG_1",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ },
+ {
+ "name": "AG_2",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax2"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/data-flow.json b/sdmx-json10/src/test/resources/serialization.expected/data-flow.json
new file mode 100644
index 0000000..77d29de
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/data-flow.json
@@ -0,0 +1,40 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "isFinal": true,
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ },
+ "links": [
+ {
+ "uri": "http://some.com"
+ }
+ ]
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=IMF:ARTEFACT(1.2)",
+ "type": "dataflow",
+ "rel": "self"
+ }
+ ],
+ "structure": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.2)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/data-provider-scheme.json b/sdmx-json10/src/test/resources/serialization.expected/data-provider-scheme.json
new file mode 100644
index 0000000..ebcc6b0
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/data-provider-scheme.json
@@ -0,0 +1,92 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.base.DataProviderScheme=IMF:DATA_PROVIDERS(1.0)",
+ "type": "dataproviderscheme",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "DATA_PROVIDERS",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.0",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "isPartial": true,
+ "isFinal": true,
+ "dataProviders": [
+ {
+ "annotations": [
+ {
+ "id": "Ann",
+ "value": "Val",
+ }
+ ],
+ "id": "Prov",
+ "name": "D_P",
+ "names": {
+ "en": "D_P"
+ },
+ "contacts": [
+ {
+ "name": "AG_1",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ },
+ {
+ "name": "AG_2",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax2"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/data-structure-definition-order.json b/sdmx-json10/src/test/resources/serialization.expected/data-structure-definition-order.json
new file mode 100644
index 0000000..802a1c1
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/data-structure-definition-order.json
@@ -0,0 +1,147 @@
+{
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "version": "1.2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "validTo": "2022-07-17T10:11:16.000000",
+ "validFrom": "2022-07-17T10:11:16.000000",
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "type": "datastructure",
+ "rel": "self"
+ }
+ ],
+ "metadata": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "dataStructureComponents": {
+ "measureList": {
+ "annotations": [],
+ "id": "MeasureDescriptor",
+ "measures": [
+ {
+ "annotations": [],
+ "id": "MES1",
+ "isMandatory": false,
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "attributeList": {
+ "annotations": [],
+ "id": "AttributeDescriptor",
+ "metadataAttributeUsages": [
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-1",
+ "attributeRelationship": {
+ "observation": {}
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-2",
+ "attributeRelationship": {
+ "dimensions": [
+ "dim1",
+ "dim2"
+ ]
+ }
+ },
+ {
+ "annotations": [],
+ "metadataAttributeReference": "REFERENCE-3",
+ "attributeRelationship": {
+ "none": {}
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "annotations": [],
+ "id": "DA1",
+ "isMandatory": false,
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "attributeRelationship": {
+ "group": "GroupKey"
+ },
+ "measureRelationship": [
+ "MO"
+ ],
+ "localRepresentation": {
+ "format": {
+ "dataType": "AlphaNumeric",
+ "isMultiLingual": false
+ }
+ }
+ }
+ ]
+ },
+ "dimensionList": {
+ "annotations": [],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "annotations": [],
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "annotations": [],
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "format": {
+ "dataType": "BasicTimePeriod"
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/data-structure-definition.json b/sdmx-json10/src/test/resources/serialization.expected/data-structure-definition.json
new file mode 100644
index 0000000..00fb6af
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/data-structure-definition.json
@@ -0,0 +1,113 @@
+{
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "type": "type1"
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "dataStructureComponents": {
+ "groups": [
+ {
+ "id": "GROUP1",
+ "groupDimensions": [
+ "DIM1"
+ ]
+ }
+ ],
+ "attributeList": {
+ "attributes": [
+ {
+ "assignmentStatus": "Conditional",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS_COLLECTION_SCHEME(1.5.1).DATE_VALUE",
+ "attributeRelationship": {
+ "dimensions": [
+ "DIM1"
+ ],
+ "attachmentGroups": [
+ "GROUP1"
+ ]
+ },
+ "localRepresentation": {
+ "textFormat": {
+ "textType": "AlphaNumeric"
+ }
+ },
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE",
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER_DIAGNOSTICS(1.5.2).DATE_VALUE"
+ ],
+ "id": "DA1"
+ }
+ ],
+ "id": "AttributeDescriptor"
+ },
+ "dimensionList": {
+ "timeDimensions": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "textFormat": {
+ "textType": "BasicTimePeriod"
+ }
+ },
+ "id": "TIME_PERIOD",
+ "position": 2,
+ "type": "TimeDimension"
+ }
+ ],
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ },
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "id": "DIM1",
+ "position": 1,
+ "type": "Dimension"
+ }
+ ]
+ },
+ "measureList": {
+ "primaryMeasure": {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "localRepresentation": {
+ "textFormat": {
+ "textType": "BasicTimePeriod"
+ }
+ },
+ "id": "MES1"
+ },
+ "id": "MeasureDescriptor"
+ }
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "datastructure"
+ }
+ ],
+ "id": "ARTEFACT",
+ "isFinal": true,
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/dimension-list.json b/sdmx-json10/src/test/resources/serialization.expected/dimension-list.json
new file mode 100644
index 0000000..1d6de29
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/dimension-list.json
@@ -0,0 +1,27 @@
+{
+ "dimensionList": {
+ "id": "DimensionDescriptor",
+ "dimensions": [
+ {
+ "position": 1,
+ "id": "DIM1",
+ "type": "Dimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DATE_VALUE",
+ "conceptRoles": [
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:ADVANCED_RELEASE_CALANDER(1.5.1).DATE_VALUE"
+ ],
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ }
+ }
+ ],
+ "timeDimensions": [
+ {
+ "position": 2,
+ "id": "TIME_PERIOD",
+ "type": "TimeDimension",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:TIME_DIMENSION(1.6.1).DATE_VALUE"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/dimension-list_empty.json b/sdmx-json10/src/test/resources/serialization.expected/dimension-list_empty.json
new file mode 100644
index 0000000..077919e
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/dimension-list_empty.json
@@ -0,0 +1,3 @@
+{
+ "dimensionList": null
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/empty-structure.json b/sdmx-json10/src/test/resources/serialization.expected/empty-structure.json
new file mode 100644
index 0000000..bf9ef71
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/empty-structure.json
@@ -0,0 +1,15 @@
+{
+ "data": {},
+ "meta": {
+ "schema": "https://raw.githubusercontent.com/sdmx-twg/sdmx-json/master/metadata-message/tools/schemas/2.0.0/sdmx-json-metadata-schema.json",
+ "id": "IDREF7553",
+ "test": false,
+ "prepared": "2022-04-03T15:35:01Z",
+ "contentLanguages": [
+ "en"
+ ],
+ "sender": {
+ "id": "unknown"
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/expected-flat-json-streamer-response.json b/sdmx-json10/src/test/resources/serialization.expected/expected-flat-json-streamer-response.json
new file mode 100644
index 0000000..a7e7435
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/expected-flat-json-streamer-response.json
@@ -0,0 +1,110 @@
+{
+ "meta": {},
+ "data": {
+ "dataSets": [
+ {
+ "structure": 0,
+ "action": "Information",
+ "observations": {
+ "0:0": [
+ "obsValue2"
+ ],
+ "0:1": [
+ "obsValue5"
+ ],
+ "1:1": [
+ "obsValue6"
+ ]
+ }
+ }
+ ],
+ "structures": [
+ {
+ "dataSets": [
+ 0
+ ],
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=QH:DF(4.20)",
+ "title": "Dataflow"
+ },
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QH:DSD(4.20)",
+ "title": "DataStructureDefinition"
+ }
+ ],
+ "dimensions": {
+ "series": [
+ {
+ "id": "dim1",
+ "keyPosition": 0,
+ "roles": [],
+ "values": [
+ {
+ "value": "dim1-val1"
+ },
+ {
+ "value": "dim1-val2"
+ }
+ ]
+ },
+ {
+ "id": "dim2",
+ "keyPosition": 1,
+ "roles": [
+ "role"
+ ],
+ "values": [
+ {
+ "value": "dim2-val1"
+ },
+ {
+ "value": "dim2-val2"
+ }
+ ]
+ }
+ ]
+ },
+ "measures": {
+ "observation": [
+ {
+ "id": "OBS_VALUE",
+ "roles": []
+ }
+ ]
+ },
+ "attributes": {
+ "dataSet": [
+ {
+ "id": "dataset",
+ "roles": [],
+ "isMandatory": false,
+ "relationship": {
+ "dataflow": {}
+ }
+ }
+ ],
+ "observation": [
+ {
+ "id": "observation1",
+ "roles": [],
+ "isMandatory": false,
+ "relationship": {
+ "observation": {}
+ }
+ },
+ {
+ "id": "observation2",
+ "roles": [],
+ "isMandatory": false,
+ "relationship": {
+ "observation": {}
+ }
+ }
+ ]
+ },
+ "annotations": []
+ }
+ ]
+ }
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/geocodelist.json b/sdmx-json10/src/test/resources/serialization.expected/geocodelist.json
new file mode 100644
index 0000000..7ea6397
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/geocodelist.json
@@ -0,0 +1,92 @@
+{
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "geoFeatureSetCodes": [
+ {
+ "parent": "CODE2",
+ "names": {
+ "en": "code 1"
+ },
+ "name": "code 1",
+ "annotations": [],
+ "description": "code 1 desc",
+ "id": "CODE1",
+ "descriptions": {
+ "en": "code 1 desc"
+ },
+ "value": "Value1"
+ },
+ {
+ "names": {
+ "en": "code 2"
+ },
+ "name": "code 2",
+ "annotations": [],
+ "description": "code 2 desc",
+ "id": "CODE2",
+ "descriptions": {
+ "en": "code 2 desc"
+ },
+ "value": "Value2"
+ }
+ ],
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "codelistExtensions": [
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "prefix": "PREF2",
+ "inclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "cascadeValues": "true",
+ "value": "mem1"
+ }
+ ]
+ }
+ },
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "prefix": "PREF2",
+ "exclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "cascadeValues": "false",
+ "value": "mem2"
+ }
+ ]
+ }
+ }
+ ],
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "geoType": "GeographicCodelist",
+ "name": "Maintanable artefact",
+ "isPartial": true,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.GeographicCodelist=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "geographiccodelist"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/geogridcodelist.json b/sdmx-json10/src/test/resources/serialization.expected/geogridcodelist.json
new file mode 100644
index 0000000..bea7773
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/geogridcodelist.json
@@ -0,0 +1,93 @@
+{
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "geoGridCodes": [
+ {
+ "parent": "CODE2",
+ "names": {
+ "en": "code 1"
+ },
+ "geoCell": "Value1",
+ "name": "code 1",
+ "annotations": [],
+ "description": "code 1 desc",
+ "id": "CODE1",
+ "descriptions": {
+ "en": "code 1 desc"
+ }
+ },
+ {
+ "names": {
+ "en": "code 2"
+ },
+ "geoCell": "Value2",
+ "name": "code 2",
+ "annotations": [],
+ "description": "code 2 desc",
+ "id": "CODE2",
+ "descriptions": {
+ "en": "code 2 desc"
+ }
+ }
+ ],
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "codelistExtensions": [
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "prefix": "PREF2",
+ "inclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "cascadeValues": "true",
+ "value": "mem1"
+ }
+ ]
+ }
+ },
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=SDMX:CL_AGE(2.1.1)",
+ "prefix": "PREF2",
+ "exclusiveCodeSelection": {
+ "memberValues": [
+ {
+ "cascadeValues": "false",
+ "value": "mem2"
+ }
+ ]
+ }
+ }
+ ],
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "geoType": "GeoGridCodelist",
+ "name": "Maintanable artefact",
+ "isPartial": true,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.GeoGridCodelist=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "geogridcodelist"
+ }
+ ],
+ "id": "ARTEFACT",
+ "gridDefinition": "DEFINITION",
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/group-dimension-list.json b/sdmx-json10/src/test/resources/serialization.expected/group-dimension-list.json
new file mode 100644
index 0000000..369d450
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/group-dimension-list.json
@@ -0,0 +1,26 @@
+{
+ "groups": [
+ {
+ "annotations": [
+ {
+ "id": "id1"
+ },
+ {
+ "id": "id2"
+ }
+ ],
+ "id": "GroupDimensionDescriptor1",
+ "groupDimensions": [
+ "dim1",
+ "dim12"
+ ]
+ },
+ {
+ "id": "GroupDimensionDescriptor2",
+ "groupDimensions": [
+ "dim21",
+ "dim22"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/hierarchy.json b/sdmx-json10/src/test/resources/serialization.expected/hierarchy.json
new file mode 100644
index 0000000..6398467
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/hierarchy.json
@@ -0,0 +1,87 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Hierarchy=IMF:ARTEFACT(1.2)",
+ "type": "hierarchy",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "isFinal": true,
+ "agencyID": "IMF",
+ "hierarchies": [
+ {
+ "id": "Hierarchy",
+ "name": "Hierarchy",
+ "hierarchicalCodes": [
+ {
+ "annotations": [
+ {
+ "id": "id"
+ }
+ ],
+ "id": "id",
+ "validFrom": "2022-07-17T10:11:16.345Z",
+ "validTo": "2022-07-17T10:11:16.345Z",
+ "code": "urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1",
+ "codeID": "CL1",
+ "level": "Lev1",
+ "hierarchicalCodes": [
+ {
+ "id": "1",
+ "validFrom": "2022-07-17T10:11:16.345Z",
+ "validTo": "2022-07-17T10:11:16.345Z",
+ "code": "urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1",
+ "codeID": "CL1",
+ "level": null
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "level": {
+ "id": "levId",
+ "name": "hierarchy name",
+ "names": {
+ "en": "hierarchy name"
+ },
+ "description": "hierarchy desc",
+ "descriptions": {
+ "en": "hierarchy desc"
+ },
+ "codingFormat": {
+ "interval": 1
+ },
+ "level": {
+ "id": "levUn",
+ "name": "underLevel",
+ "names": {
+ "en": "underLevel"
+ },
+ "codingFormat": {}
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/hierarchy_flat.json b/sdmx-json10/src/test/resources/serialization.expected/hierarchy_flat.json
new file mode 100644
index 0000000..544dc15
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/hierarchy_flat.json
@@ -0,0 +1,77 @@
+{
+ "hierarchies": [
+ {
+ "name": "Hierarchy",
+ "hierarchicalCodes": [
+ {
+ "codeID": "CL1",
+ "code": "urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL1",
+ "level": "Lev1",
+ "annotations": [
+ {
+ "id": "id"
+ }
+ ],
+ "id": "id",
+ "validFrom": "2022-07-17T10:11:16.345Z",
+ "validTo": "2022-07-17T10:11:16.345Z"
+ },
+ {
+ "codeID": "CL2",
+ "code": "urn:sdmx:org.sdmx.infomodel.codelist.Code=SDMX:CL_AGE(2.1.1).CL2",
+ "level": "Lev2",
+ "id": "hi-2",
+ "validFrom": "2022-07-17T10:11:16.345Z",
+ "validTo": "2022-07-17T10:11:16.345Z"
+ }
+ ],
+ "id": "Hierarchy"
+ }
+ ],
+ "level": {
+ "names": {
+ "en": "hierarchy name"
+ },
+ "codingFormat": {
+ "interval": 1
+ },
+ "name": "hierarchy name",
+ "description": "hierarchy desc",
+ "id": "levId",
+ "descriptions": {
+ "en": "hierarchy desc"
+ }
+ },
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "type": "type1"
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Hierarchy=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "hierarchy"
+ }
+ ],
+ "id": "ARTEFACT",
+ "isFinal": true,
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/measure-list.json b/sdmx-json10/src/test/resources/serialization.expected/measure-list.json
new file mode 100644
index 0000000..f56bf05
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/measure-list.json
@@ -0,0 +1,14 @@
+{
+ "measureList": {
+ "id": "MeasureDescriptor",
+ "primaryMeasure": {
+ "id": "MES1",
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:MEASURE(1.5.2).DATE_VALUE",
+ "localRepresentation": {
+ "textFormat": {
+ "textType": "BasicTimePeriod"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/measure-list_empty.json b/sdmx-json10/src/test/resources/serialization.expected/measure-list_empty.json
new file mode 100644
index 0000000..679b765
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/measure-list_empty.json
@@ -0,0 +1,3 @@
+{
+ "measureList": null
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/meta-data-constraint.json b/sdmx-json10/src/test/resources/serialization.expected/meta-data-constraint.json
new file mode 100644
index 0000000..ce5dcd3
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/meta-data-constraint.json
@@ -0,0 +1,69 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.registry.MetadataConstraint=IMF:ARTEFACT(1.2)",
+ "type": "metadataconstraint",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "isFinal": true,
+ "agencyID": "IMF",
+ "constraintAttachment": {
+ "dataProvider": "urn:sdmx:org.sdmx.infomodel.base.DataProvider=IMF:DATA_PROVIDERS(1.0).DP",
+ "metadataStructures": [
+ "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=QUANTHUB:ECOFIN_DSD(1.2)"
+ ]
+ },
+ "role": "Allowed",
+ "releaseCalendar": {
+ "tolerance": "Tolerance",
+ "offset": "Offset",
+ "periodicity": "Periodicity"
+ },
+ "metadataTargetRegions": [
+ {
+ "include": true,
+ "attributes": [
+ {
+ "id": "MS",
+ "values": [
+ "MEM",
+ "local"
+ ]
+ },
+ {
+ "id": "MS2",
+ "timeRange": {
+ "beforePeriod": {
+ "period": "2023",
+ "isInclusive": true
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/metadata-constraint.json b/sdmx-json10/src/test/resources/serialization.expected/metadata-constraint.json
new file mode 100644
index 0000000..de408c2
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/metadata-constraint.json
@@ -0,0 +1,77 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.registry.MetadataConstraint=IMF:ARTEFACT(1.2)",
+ "type": "metadataconstraint",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "role": "Allowed",
+ "metadataTargetRegions": [
+ {
+ "include": true,
+ "validFrom": "2022-07-17T10:11:16Z",
+ "validTo": "2022-07-17T10:11:16Z",
+ "components": [
+ {
+ "include": false,
+ "id": "MS",
+ "removePrefix": true,
+ "values": [
+ {
+ "value": "MEM",
+ "cascadeValues": "excluderoot",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "validTo": "2022-09-17T10:11:16Z"
+ }
+ ]
+ },
+ {
+ "include": false,
+ "id": "MS2",
+ "removePrefix": true,
+ "timeRange": {
+ "beforePeriod": {
+ "period": "2023",
+ "isInclusive": true
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "releaseCalendar": {
+ "tolerance": "Tolerance",
+ "offset": "Offset",
+ "periodicity": "Periodicity"
+ },
+ "constraintAttachment": {
+ "metadataProvider": "urn:sdmx:org.sdmx.infomodel.base.MetadataProvider=IMF:METADATA_PROVIDERS(1.0).PR"
+ }
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/metadata-flow.json b/sdmx-json10/src/test/resources/serialization.expected/metadata-flow.json
new file mode 100644
index 0000000..231de6d
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/metadata-flow.json
@@ -0,0 +1,39 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=IMF:ARTEFACT(1.2)",
+ "type": "metadataflow",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "isFinal": true,
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "structure": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=IMF:METADATA(1.0.0)",
+ "targets": [
+ "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.2)",
+ "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=QUANTHUB:ECOFIN_DSD(1.3)"
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/metadata-provider-scheme.json b/sdmx-json10/src/test/resources/serialization.expected/metadata-provider-scheme.json
new file mode 100644
index 0000000..8a5a31d
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/metadata-provider-scheme.json
@@ -0,0 +1,96 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.base.MetadataProviderScheme=IMF:METADATA_PROVIDERS(1.0)",
+ "type": "metadataproviderscheme",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "METADATA_PROVIDERS",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.0",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "isPartial": true,
+ "metadataProviders": [
+ {
+ "annotations": [
+ {
+ "id": "Ann",
+ "title": null,
+ "type": null,
+ "value": "Val",
+ "text": null
+ }
+ ],
+ "id": "Prov",
+ "name": "MD_P",
+ "names": {
+ "en": "MD_P"
+ },
+ "contacts": [
+ {
+ "name": "AG_1",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ },
+ {
+ "name": "AG_2",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax2"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/metadata-provision-agreement.json b/sdmx-json10/src/test/resources/serialization.expected/metadata-provision-agreement.json
new file mode 100644
index 0000000..a886681
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/metadata-provision-agreement.json
@@ -0,0 +1,37 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.registry.MetadataProvisionAgreement=IMF:ARTEFACT(1.2)",
+ "type": "metadataprovisionagreement",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "metadataProvider": "urn:sdmx:org.sdmx.infomodel.base.MetadataProvider=IMF:METADATA_PROVIDERS(1.0).PR",
+ "metadataflow": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=IMF:ARTEFACT(1.2)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/metadata-structure-definition.json b/sdmx-json10/src/test/resources/serialization.expected/metadata-structure-definition.json
new file mode 100644
index 0000000..aaddb21
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/metadata-structure-definition.json
@@ -0,0 +1,68 @@
+{
+ "metadataStructureComponents": {
+ "metadataAttributeList": {
+ "annotations": [
+ {
+ "id": "Annot-1"
+ }
+ ],
+ "metadataAttributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DIM",
+ "minOccurs": 10,
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ },
+ "maxOccurs": 20,
+ "metadataAttributes": [
+ {
+ "conceptIdentity": "urn:sdmx:org.sdmx.infomodel.conceptscheme.Concept=IMF:DIMENSION(1.6.1).DIM",
+ "minOccurs": 10,
+ "localRepresentation": {
+ "enumeration": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST(1.0)"
+ },
+ "maxOccurs": 20,
+ "id": "meta-2",
+ "isPresentational": true
+ }
+ ],
+ "id": "meta-1",
+ "isPresentational": true
+ }
+ ],
+ "id": "MetadataAttributeDescriptor"
+ }
+ },
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "type": "type1"
+ }
+ ],
+ "description": "Some artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "metadatastructure"
+ }
+ ],
+ "agencyID": "IMF",
+ "id": "ARTEFACT",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "isFinal": true
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/metadata.json b/sdmx-json10/src/test/resources/serialization.expected/metadata.json
new file mode 100644
index 0000000..2c50209
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/metadata.json
@@ -0,0 +1,75 @@
+{
+ "data": {
+ "metadataSets": [
+ {
+ "metadataflow": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=EPM:MetadataSet(1.8)",
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "reportingEnd": "2022",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "targets": [
+ "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=EPM:MetadataSetTar(1.8)"
+ ],
+ "reportingBegin": "2022",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "publicationPeriod": "2022-03",
+ "publicationYear": "2022",
+ "action": "Append",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "metadataset"
+ }
+ ],
+ "attributes": [
+ {
+ "id": "attr1",
+ "value": "someVal",
+ "attributes": [
+ {
+ "attributes": [],
+ "id": "attr2",
+ "value": "someVal2"
+ }
+ ]
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16Z"
+ }
+ ]
+ },
+ "meta": {
+ "schema": "https://raw.githubusercontent.com/sdmx-twg/sdmx-json/master/metadata-message/tools/schemas/2.0.0/sdmx-json-metadata-schema.json",
+ "id": "IDREF8124",
+ "test": false,
+ "prepared": "2021-11-02T09:47:41Z",
+ "contentLanguages": [
+ "en"
+ ],
+ "sender": {
+ "id": "unknown"
+ }
+ }
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/metadataset.json b/sdmx-json10/src/test/resources/serialization.expected/metadataset.json
new file mode 100644
index 0000000..7db25c2
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/metadataset.json
@@ -0,0 +1,56 @@
+{
+ "metadataflow": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=EPM:MetadataSet(1.8)",
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "reportingEnd": "2022",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "targets": [
+ "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=EPM:MetadataSetTar(1.8)"
+ ],
+ "reportingBegin": "2022",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "publicationPeriod": "2022-03",
+ "publicationYear": "2022",
+ "action": "Append",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "metadataset"
+ }
+ ],
+ "attributes": [
+ {
+ "id": "attr1",
+ "value": "someVal",
+ "attributes": [
+ {
+ "id": "attr2",
+ "value": "someVal2"
+ }
+ ]
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/organisation-scheme-map.json b/sdmx-json10/src/test/resources/serialization.expected/organisation-scheme-map.json
new file mode 100644
index 0000000..a9dc749
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/organisation-scheme-map.json
@@ -0,0 +1,60 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.OrganisationSchemeMap=IMF:ARTEFACT(1.2)",
+ "type": "organisationschememap",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "isFinal": true,
+ "itemMaps": [
+ {
+ "annotations": [
+ {
+ "id": "Ann"
+ }
+ ],
+ "sourceValue": "S-1",
+ "targetValue": "IM-1",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ },
+ {
+ "annotations": [
+ {
+ "id": "Ann"
+ }
+ ],
+ "sourceValue": "S-2",
+ "targetValue": "IM-2",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ }
+ ],
+ "source": "urn:sdmx:org.sdmx.infomodel.base.AgencyScheme=SDMX:AGENCIES(1.0)",
+ "target": "urn:sdmx:org.sdmx.infomodel.base.AgencyScheme=SDMX:AGENCIES(1.0)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/organisation-unit-scheme.json b/sdmx-json10/src/test/resources/serialization.expected/organisation-unit-scheme.json
new file mode 100644
index 0000000..b8dd3c3
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/organisation-unit-scheme.json
@@ -0,0 +1,103 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.base.OrganisationUnitScheme=IMF:ARTEFACT(1.0)",
+ "type": "organisationunitscheme",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "type": "type1",
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.0",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "isPartial": true,
+ "isFinal": true,
+ "organisationUnits": [
+ {
+ "annotations": [
+ {
+ "id": "Annot"
+ }
+ ],
+ "id": "OrgUnit",
+ "name": "OrganisationUnit",
+ "names": {
+ "en": "OrganisationUnit"
+ },
+ "description": "OrganisationUnit description",
+ "descriptions": {
+ "en": "OrganisationUnit description"
+ },
+ "contacts": [
+ {
+ "name": "AG_1",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ },
+ {
+ "name": "AG_2",
+ "department": "IT",
+ "role": {
+ "en": "writing",
+ "fr": "ecrire"
+ },
+ "telephones": [
+ "9458673082"
+ ],
+ "faxes": [
+ "somedatafax2"
+ ],
+ "x400s": [
+ "X400"
+ ],
+ "emails": [
+ "test@email.com"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "OrgUnit2",
+ "parent": "OrgUnit",
+ "name": "OrganisationUnit",
+ "names": {
+ "en": "OrganisationUnit"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/process.json b/sdmx-json10/src/test/resources/serialization.expected/process.json
new file mode 100644
index 0000000..b89fe09
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/process.json
@@ -0,0 +1,136 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.process.Process=IMF:ARTEFACT(1.2)",
+ "type": "process",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "processSteps": [
+ {
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "id": "ProcStep",
+ "name": "ProcessStep",
+ "names": {
+ "en": "ProcessStep"
+ },
+ "description": "ProcessStep description",
+ "descriptions": {
+ "en": "ProcessStep description"
+ },
+ "computation": {
+ "localID": "LocId",
+ "softwareLanguage": "SoftwareLanguage",
+ "softwarePackage": "SoftwarePackage",
+ "softwareVersion": "SoftwareVersion",
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "description": "Computation Description",
+ "descriptions": {
+ "en": "Computation Description"
+ }
+ },
+ "transitions": [
+ {
+ "annotations": [],
+ "id": "Trans",
+ "localID": "Loc",
+ "condition": "Doo",
+ "conditions": {
+ "en": "Doo"
+ },
+ "targetStep": "ST"
+ }
+ ],
+ "outputs": [
+ {
+ "localID": "Loc",
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "objectReference": "urn:sdmx:org.sdmx.infomodel.process.ProcessStep=IMF:Artefact(1.0)"
+ }
+ ],
+ "inputs": [
+ {
+ "localID": "Loc",
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "objectReference": "urn:sdmx:org.sdmx.infomodel.process.ProcessStep=IMF:Artefact(1.0)"
+ }
+ ],
+ "processSteps": [
+ {
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "id": "ProcessChild",
+ "name": "ProcessStepChild",
+ "names": {
+ "en": "ProcessStepChild"
+ }
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/provision-agreement.json b/sdmx-json10/src/test/resources/serialization.expected/provision-agreement.json
new file mode 100644
index 0000000..9f308f4
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/provision-agreement.json
@@ -0,0 +1,37 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.registry.ProvisionAgreement=IMF:ARTEFACT(1.2)",
+ "type": "provisionagreement",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "dataProvider": "urn:sdmx:org.sdmx.infomodel.base.DataProvider=IMF:DATA_PROVIDERS(1.0).DP",
+ "dataflow": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=IMF:ARTEFACT(1.2)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/reporting-taxonomy-map.json b/sdmx-json10/src/test/resources/serialization.expected/reporting-taxonomy-map.json
new file mode 100644
index 0000000..dc4d0fa
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/reporting-taxonomy-map.json
@@ -0,0 +1,69 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.ReportingTaxonomyMap=IMF:ARTEFACT(1.2)",
+ "type": "reportingtaxonomymap",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "itemMaps": [
+ {
+ "annotations": [
+ {
+ "id": "Ann",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "sourceValue": "S-1",
+ "targetValue": "IM-1",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ },
+ {
+ "annotations": [
+ {
+ "id": "Ann",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "sourceValue": "S-2",
+ "targetValue": "IM-2",
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ }
+ ],
+ "source": "urn:sdmx:org.sdmx.infomodel.categoryscheme.ReportingTaxonomy=SDMX:REP_SOURCE(1.0)",
+ "target": "urn:sdmx:org.sdmx.infomodel.categoryscheme.ReportingTaxonomy=SDMX:REP_TARGET(1.0)"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/reporting-taxonomy.json b/sdmx-json10/src/test/resources/serialization.expected/reporting-taxonomy.json
new file mode 100644
index 0000000..9dda79c
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/reporting-taxonomy.json
@@ -0,0 +1,88 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.ReportingTaxonomy=IMF:ARTEFACT(1.2)",
+ "type": "reportingtaxonomy",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "isPartial": false,
+ "reportingCategories": [
+ {
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "id": "RC-1",
+ "name": "ReportingCat",
+ "names": {
+ "en": "ReportingCat"
+ },
+ "description": "ReportingCat description",
+ "descriptions": {
+ "en": "ReportingCat description"
+ },
+ "provisioningMetadata": [
+ "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataS2(3.0)",
+ "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataS1(3.0)"
+ ],
+ "reportingCategories": [
+ {
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "id": "RC-2",
+ "name": "ReportingCat2",
+ "names": {
+ "en": "ReportingCat2"
+ },
+ "description": "ReportingCat2 description",
+ "descriptions": {
+ "en": "ReportingCat2 description"
+ },
+ "structuralMetadata": [
+ "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=EPM:DataS2(3.0)",
+ "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=EPM:DataS1(3.0)"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/representation-map.json b/sdmx-json10/src/test/resources/serialization.expected/representation-map.json
new file mode 100644
index 0000000..ce6f779
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/representation-map.json
@@ -0,0 +1,67 @@
+{
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "source": [
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST_SOURCE(1.0)"
+ },
+ {
+ "dataType": "DateTime"
+ }
+ ],
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "target": [
+ {
+ "codelist": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=IMF:CODELIST_TARGET(1.0)"
+ }
+ ],
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "representationMappings": [
+ {
+ "targetValues": [
+ "targetValue1",
+ "targetValue2"
+ ],
+ "sourceValues": [
+ {
+ "startIndex": 0,
+ "isRegEx": true,
+ "endIndex": 10,
+ "value": "mappedValue"
+ }
+ ],
+ "annotations": [],
+ "validFrom": "2022-07-17T00:00:00Z",
+ "validTo": "2022-07-17T00:00:00Z"
+ }
+ ],
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "representationmap"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/structure-map-with-null-map.json b/sdmx-json10/src/test/resources/serialization.expected/structure-map-with-null-map.json
new file mode 100644
index 0000000..423c13f
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/structure-map-with-null-map.json
@@ -0,0 +1,65 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=IMF:ARTEFACT(1.2)",
+ "type": "structuremap",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "epochMaps": [],
+ "datePatternMaps": [],
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_3",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR3"
+ },
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR1"
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR2"
+ }
+ ],
+ "fixedValueMaps": [
+ {
+ "annotations": [],
+ "value": "value"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/structure-map-with-null-target-source.json b/sdmx-json10/src/test/resources/serialization.expected/structure-map-with-null-target-source.json
new file mode 100644
index 0000000..ac6343f
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/structure-map-with-null-target-source.json
@@ -0,0 +1,120 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=IMF:ARTEFACT(1.2)",
+ "type": "structuremap",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "epochMaps": [
+ {
+ "annotations": [],
+ "id": "EPOCH1",
+ "mappedComponents": [
+ {
+ "source": "CompMapEpochS",
+ "target": "CompMapEpochT"
+ }
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "frequencyDimension": "Frequency_dim",
+ "mappedFrequencies": [
+ "FR3"
+ ],
+ "yearStart": {},
+ "basePeriod": "2022-07-17T10:11:16Z",
+ "epochPeriod": "MICROSECOND"
+ }
+ ],
+ "datePatternMaps": [
+ {
+ "annotations": [],
+ "id": "DP1",
+ "mappedComponents": [
+ {
+ "source": "CompMapDPS",
+ "target": "CompMapDPT"
+ }
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "frequencyDimension": "Frequency_dim",
+ "mappedFrequencies": [
+ "FR1",
+ "FR2"
+ ],
+ "yearStart": {},
+ "locale": "EN",
+ "sourcePattern": "SOURCE"
+ }
+ ],
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_3",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR3"
+ },
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR1"
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR2"
+ }
+ ],
+ "componentMaps": [
+ {
+ "annotations": [],
+ "source": [
+ "CompMapRepS"
+ ],
+ "target": [
+ "CompMapRepT"
+ ],
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)"
+ },
+ {
+ "annotations": [],
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)"
+ }
+ ],
+ "fixedValueMaps": [
+ {
+ "annotations": [],
+ "value": "value"
+ }
+ ]
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/structure-map.json b/sdmx-json10/src/test/resources/serialization.expected/structure-map.json
new file mode 100644
index 0000000..6203edc
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/structure-map.json
@@ -0,0 +1,116 @@
+{
+ "frequencyFormatMappings": [
+ {
+ "frequencyId": "CODE_3",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR3"
+ },
+ {
+ "frequencyId": "CODE_1",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR1"
+ },
+ {
+ "frequencyId": "CODE_2",
+ "datePattern": "2022-11",
+ "annotations": [],
+ "id": "FR2"
+ }
+ ],
+ "datePatternMaps": [
+ {
+ "mappedFrequencies": [
+ "FR1",
+ "FR2"
+ ],
+ "mappedComponents": [
+ {
+ "source": "CompMapDPS",
+ "target": "CompMapDPT"
+ }
+ ],
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "DP1",
+ "locale": "EN",
+ "frequencyDimension": "Frequency_dim",
+ "yearStart": {},
+ "sourcePattern": "SOURCE"
+ }
+ ],
+ "fixedValueMaps": [
+ {
+ "annotations": [],
+ "value": "value"
+ }
+ ],
+ "epochMaps": [
+ {
+ "mappedFrequencies": [
+ "FR3"
+ ],
+ "basePeriod": "2022-07-17T10:11:16Z",
+ "mappedComponents": [
+ {
+ "source": "CompMapEpochS",
+ "target": "CompMapEpochT"
+ }
+ ],
+ "epochPeriod": "MICROSECOND",
+ "resolvePeriod": "MID_PERIOD",
+ "annotations": [],
+ "id": "EPOCH1",
+ "frequencyDimension": "Frequency_dim",
+ "yearStart": {}
+ }
+ ],
+ "componentMaps": [
+ {
+ "representationMap": "urn:sdmx:org.sdmx.infomodel.structuremapping.RepresentationMap=IMF:ARTEFACT(1.2)",
+ "annotations": [],
+ "source": [
+ "CompMapRepS"
+ ],
+ "target": [
+ "CompMapRepT"
+ ]
+ }
+ ],
+ "annotations": [
+ {
+ "texts": {
+ "en": "text"
+ },
+ "id": "1",
+ "text": "text",
+ "title": null,
+ "type": "type1",
+ "value": null
+ }
+ ],
+ "description": "Some artefact",
+ "agencyID": "IMF",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "source": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFSource(3.0)",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "target": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=EPM:DataFTarget(3.0)",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "name": "Maintanable artefact",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.structuremapping.StructureMap=IMF:ARTEFACT(1.2)",
+ "rel": "self",
+ "type": "structuremap"
+ }
+ ],
+ "id": "ARTEFACT",
+ "validTo": "2022-07-17T10:11:16Z"
+}
\ No newline at end of file
diff --git a/sdmx-json10/src/test/resources/serialization.expected/structure.json b/sdmx-json10/src/test/resources/serialization.expected/structure.json
new file mode 100644
index 0000000..b502b8c
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/structure.json
@@ -0,0 +1,148 @@
+{
+ "data": {
+ "categorisations": [
+ {
+ "description": "categ",
+ "agencyID": "EPM",
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=EPM:catid(1.0)",
+ "rel": "self",
+ "type": "categorisation"
+ }
+ ],
+ "id": "catid",
+ "source": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Categorisation=IMF:ARTEFACT(1.2).CAT_SOURCE",
+ "descriptions": {
+ "en": "categ"
+ },
+ "version": "1.0",
+ "isFinal": true,
+ "target": "urn:sdmx:org.sdmx.infomodel.categoryscheme.Category=IMF:ARTEFACT(1.2).CAT_TARGET"
+ }
+ ],
+ "dataStructures": [
+ {
+ "dataStructureComponents": null,
+ "agencyID": null,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.DataStructure=null:DSD1(1.0)",
+ "rel": "self",
+ "type": "datastructure"
+ }
+ ],
+ "id": "DSD1",
+ "version": "1.0",
+ "isFinal": true
+ }
+ ],
+ "hierarchicalCodelists": [
+ {
+ "agencyID": null,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Hierarchy=null:Hi1(1.0)",
+ "rel": "self",
+ "type": "hierarchy"
+ }
+ ],
+ "id": "Hi1",
+ "version": "1.0",
+ "isFinal": true
+ }
+ ],
+ "categorySchemes": [
+ {
+ "isPartial": false,
+ "agencyID": null,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.categoryscheme.CategoryScheme=null:CS1(1.0)",
+ "rel": "self",
+ "type": "categoryscheme"
+ }
+ ],
+ "id": "CS1",
+ "categories": [],
+ "version": "1.0",
+ "isFinal": true
+ }
+ ],
+ "codelists": [
+ {
+ "isPartial": false,
+ "agencyID": null,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.Codelist=null:CL1(1.0)",
+ "rel": "self",
+ "type": "codelist"
+ }
+ ],
+ "id": "CL1",
+ "version": "1.0",
+ "isFinal": true
+ }
+ ],
+ "conceptSchemes": [
+ {
+ "concepts": [],
+ "isPartial": false,
+ "agencyID": null,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.conceptscheme.ConceptScheme=null:CS1(1.0)",
+ "rel": "self",
+ "type": "conceptscheme"
+ }
+ ],
+ "id": "CS1",
+ "version": "1.0",
+ "isFinal": true
+ },
+ {
+ "concepts": [],
+ "isPartial": false,
+ "agencyID": null,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.conceptscheme.ConceptScheme=null:CS2(1.0)",
+ "rel": "self",
+ "type": "conceptscheme"
+ }
+ ],
+ "id": "CS2",
+ "version": "1.0",
+ "isFinal": true
+ }
+ ],
+ "dataflows": [
+ {
+ "agencyID": null,
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=null:DF1(1.0)",
+ "rel": "self",
+ "type": "dataflow"
+ }
+ ],
+ "id": "DF1",
+ "version": "1.0",
+ "isFinal": true
+ }
+ ]
+ },
+ "meta": {
+ "schema": "https://raw.githubusercontent.com/sdmx-twg/sdmx-json/master/metadata-message/tools/schemas/2.0.0/sdmx-json-metadata-schema.json",
+ "test": false,
+ "sender": {
+ "id": "unknown"
+ },
+ "prepared": "2022-04-03T15:35:01Z",
+ "contentLanguages": [
+ "en"
+ ],
+ "id": "IDREF7553"
+ }
+}
diff --git a/sdmx-json10/src/test/resources/serialization.expected/valuelist.json b/sdmx-json10/src/test/resources/serialization.expected/valuelist.json
new file mode 100644
index 0000000..2c261a3
--- /dev/null
+++ b/sdmx-json10/src/test/resources/serialization.expected/valuelist.json
@@ -0,0 +1,79 @@
+{
+ "links": [
+ {
+ "urn": "urn:sdmx:org.sdmx.infomodel.codelist.ValueList=IMF:ARTEFACT(1.2)",
+ "type": "valuelist",
+ "rel": "self"
+ }
+ ],
+ "annotations": [
+ {
+ "id": "1",
+ "title": null,
+ "type": "type1",
+ "value": null,
+ "text": "text",
+ "texts": {
+ "en": "text"
+ }
+ }
+ ],
+ "id": "ARTEFACT",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "Maintanable artefact",
+ "fr-CA": "artefact"
+ },
+ "description": "Some artefact",
+ "descriptions": {
+ "en": "Some artefact"
+ },
+ "version": "1.2",
+ "validTo": "2022-07-17T10:11:16Z",
+ "validFrom": "2022-07-17T10:11:16Z",
+ "agencyID": "IMF",
+ "valueItems": [
+ {
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "id": "VAL-1",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "value",
+ "fr": "val"
+ },
+ "description": "it is value",
+ "descriptions": {
+ "en": "it is value",
+ "fr": "c'est val"
+ }
+ },
+ {
+ "annotations": [
+ {
+ "id": "Annot",
+ "title": null,
+ "type": null,
+ "value": null,
+ "text": null
+ }
+ ],
+ "id": "VAL-2",
+ "name": "Maintanable artefact",
+ "names": {
+ "en": "smth"
+ },
+ "description": "smth description",
+ "descriptions": {
+ "en": "smth description"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdmx-json20/build.gradle b/sdmx-json20/build.gradle
new file mode 100644
index 0000000..26fc84f
--- /dev/null
+++ b/sdmx-json20/build.gradle
@@ -0,0 +1,44 @@
+plugins {
+ id 'java'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation project(":sdmx30-serializer")
+
+ annotationProcessor("org.projectlombok:lombok:${lombok_version}")
+ compileOnly("org.projectlombok:lombok:${lombok_version}")
+
+ implementation project(":sdmx30-infomodel")
+
+ implementation("org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}")
+ implementation("org.apache.logging.log4j:log4j-core:${log4j_version}")
+ implementation("com.epam.deltix:SdmxDataParser:${sdmx_version}")
+
+ implementation("org.mapstruct:mapstruct:${mapstruct_version}")
+ annotationProcessor("org.mapstruct:mapstruct-processor:${mapstruct_version}")
+
+ implementation("com.fasterxml.jackson.core:jackson-core:${fasterxml_version}")
+ implementation("com.fasterxml.jackson.core:jackson-databind:${fasterxml_databind_version}")
+ implementation("com.google.guava:guava:${guava_version}")
+ implementation "com.opencsv:opencsv:${openc_csv_version}"
+
+ testImplementation("org.projectlombok:lombok:${lombok_version}")
+ testAnnotationProcessor("org.projectlombok:lombok:${lombok_version}")
+
+ testImplementation("org.jsonschema2pojo:jsonschema2pojo-core:${jsonschema2pojo_version}")
+ testImplementation("org.assertj:assertj-core:${assertj_version}")
+ testImplementation("net.javacrumbs.json-unit:json-unit:${json_unit_version}")
+ testImplementation("net.javacrumbs.json-unit:json-unit-assertj:${json_unit_version}")
+ testImplementation("org.mockito:mockito-core:${mockito_version}")
+ testImplementation("org.xmlunit:xmlunit-legacy:${xmlunit_legacy_version}")
+
+ implementation("org.glassfish.jaxb:jaxb-runtime:${xml_bind_jaxb_version}")
+}
+
+test {
+ useJUnitPlatform()
+}
\ No newline at end of file
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AgencySchemeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AgencySchemeReader.java
new file mode 100644
index 0000000..7d22b6b
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AgencySchemeReader.java
@@ -0,0 +1,65 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getBooleanJsonField;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Agency;
+import com.epam.jsdmx.infomodel.sdmx30.AgencyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.AgencySchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class AgencySchemeReader extends MaintainableReader {
+
+ private final OrganisationReader organisationReader;
+
+ public AgencySchemeReader(VersionableReader versionableArtefact,
+ OrganisationReader organisationReader) {
+ super(versionableArtefact);
+ this.organisationReader = organisationReader;
+ }
+
+ @Override
+ protected AgencySchemeImpl createMaintainableArtefact() {
+ return new AgencySchemeImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, AgencySchemeImpl agencyScheme) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.IS_PARTIAL:
+ agencyScheme.setPartial(getBooleanJsonField(parser));
+ break;
+ case StructureUtils.AGENCIES:
+ List agencies = ReaderUtils.getArray(parser, (this::getAgency));
+ if (CollectionUtils.isNotEmpty(agencies)) {
+ agencyScheme.setItems(agencies);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "AgencyScheme: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.AGENCY_SCHEMES;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getAgencySchemes().addAll(artefacts);
+ }
+
+ private Agency getAgency(JsonParser parser) {
+ return organisationReader.getOrganisation(parser, new AgencyImpl(), null);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AnnotableReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AnnotableReader.java
new file mode 100644
index 0000000..15abc91
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AnnotableReader.java
@@ -0,0 +1,153 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.isNullValue;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.epam.jsdmx.infomodel.sdmx30.AnnotableArtefact;
+import com.epam.jsdmx.infomodel.sdmx30.AnnotableArtefactImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Annotation;
+import com.epam.jsdmx.infomodel.sdmx30.AnnotationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalString;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalUri;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import lombok.NoArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+
+@NoArgsConstructor
+public class AnnotableReader {
+
+ public AnnotationImpl readAnnotation(JsonParser parser) throws IOException {
+ AnnotationImpl annotation = new AnnotationImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.ID:
+ setAnnotationId(parser, annotation);
+ break;
+ case StructureUtils.TITLE:
+ setAnnotationTitle(parser, annotation);
+ break;
+ case StructureUtils.VALUE:
+ setAnnotationValue(parser, annotation);
+ break;
+ case StructureUtils.TYPE:
+ setAnnotationType(parser, annotation);
+ break;
+ case StructureUtils.TEXT:
+ parser.nextToken();
+ break;
+ case StructureUtils.TEXTS:
+ setAnnotationTexts(parser, annotation);
+ break;
+ case StructureUtils.LINKS:
+ setAnnotationLinks(parser, annotation);
+ break;
+ default:
+ throw new IllegalArgumentException("No such argument in Annotations: " + fieldName);
+ }
+ }
+ return annotation;
+ }
+
+ private void setAnnotationTitle(JsonParser parser, AnnotationImpl annotation) throws IOException {
+ String title = ReaderUtils.getStringJsonField(parser);
+ if (title != null) {
+ annotation.setTitle(title);
+ }
+ }
+
+ private void setAnnotationValue(JsonParser parser, AnnotationImpl annotation) throws IOException {
+ String value = ReaderUtils.getStringJsonField(parser);
+ if (value != null) {
+ annotation.setValue(value);
+ }
+ }
+
+ private void setAnnotationType(JsonParser parser, AnnotationImpl annotation) throws IOException {
+ String type = ReaderUtils.getStringJsonField(parser);
+ if (type != null) {
+ annotation.setType(type);
+ }
+ }
+
+ private void setAnnotationLinks(JsonParser parser, AnnotationImpl annotation) throws IOException {
+ List uriList = ReaderUtils.getArray(parser, parser1 -> getURIFromLink(parser));
+ if (CollectionUtils.isNotEmpty(uriList)) {
+ annotation.setUrl(new InternationalUri(uriList.get(0)));
+ }
+ }
+
+ private void setAnnotationTexts(JsonParser parser, AnnotationImpl annotation) throws IOException {
+ Map localizedText = new HashMap<>();
+ parser.nextToken();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ localizedText.put(parser.getCurrentName(), parser.getText());
+ }
+ annotation.setText(new InternationalString(localizedText));
+ }
+
+ private void setAnnotationId(JsonParser parser, AnnotationImpl annotation) throws IOException {
+ String id = ReaderUtils.getStringJsonField(parser);
+ if (id != null) {
+ annotation.setId(id);
+ }
+ }
+
+ private URI getURIFromLink(JsonParser parser) {
+ try {
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.HREF:
+ case StructureUtils.URN:
+ case StructureUtils.REL:
+ case StructureUtils.TITLE:
+ case StructureUtils.TITLES:
+ case StructureUtils.TYPE:
+ case StructureUtils.HREFLANG:
+ parser.nextToken();
+ break;
+ case StructureUtils.URI:
+ String uriString = ReaderUtils.getStringJsonField(parser);
+ if (uriString != null) {
+ return new URI(uriString);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("No such argument in Links: " + fieldName);
+ }
+ }
+ return null;
+ } catch (IOException | URISyntaxException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ public AnnotableArtefact read(AnnotableArtefactImpl annotableArtefact, JsonParser parser) throws IOException {
+ parser.nextToken();
+ if (isNullValue(parser)) {
+ return annotableArtefact;
+ }
+ List annotationList = new ArrayList<>();
+ while (!JsonToken.END_ARRAY.equals(parser.currentToken())) {
+ if (JsonToken.START_OBJECT.equals(parser.nextToken())) {
+ AnnotationImpl annot = readAnnotation(parser);
+ annotationList.add(annot);
+ }
+ }
+ annotableArtefact.setAnnotations(annotationList);
+ return annotableArtefact;
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AttributeListReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AttributeListReader.java
new file mode 100644
index 0000000..77aa5c7
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/AttributeListReader.java
@@ -0,0 +1,200 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getStringJsonField;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.isNullValue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.AttributeDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.AttributeRelationship;
+import com.epam.jsdmx.infomodel.sdmx30.DataAttribute;
+import com.epam.jsdmx.infomodel.sdmx30.DataAttributeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionRelationshipImpl;
+import com.epam.jsdmx.infomodel.sdmx30.GroupRelationshipImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureRelationship;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureRelationshipImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataAttributeRef;
+import com.epam.jsdmx.infomodel.sdmx30.MetadataAttributeRefImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ObservationRelationshipImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Representation;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+
+public class AttributeListReader {
+
+ private final IdentifiableReader identifiableReader = new IdentifiableReader(
+ new AnnotableReader());
+
+ public AttributeDescriptorImpl getAttributeDescriptor(JsonParser parser) throws IOException {
+ parser.nextToken();
+ if (isNullValue(parser)) {
+ return null;
+ }
+ AttributeDescriptorImpl attributeDescriptor = new AttributeDescriptorImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.METADATA_ATTRIBUTE_USAGES:
+ List metadataAttributeRefs = ReaderUtils.getArray(parser, this::getMetadataAttributeRef);
+ if (CollectionUtils.isNotEmpty(metadataAttributeRefs)) {
+ List attributeRefs = metadataAttributeRefs.stream()
+ .map(MetadataAttributeRef.class::cast)
+ .collect(Collectors.toList());
+ attributeDescriptor.setMetadataAttributes(attributeRefs);
+ }
+ break;
+ case StructureUtils.ATTRIBUTES:
+ List components = getComponents(parser);
+ if (CollectionUtils.isNotEmpty(components)) {
+ attributeDescriptor.setComponents(components);
+ }
+ break;
+ default:
+ identifiableReader.read(attributeDescriptor, parser);
+ break;
+ }
+ }
+ attributeDescriptor.setId(StructureUtils.ATTRIBUTE_DESCRIPTOR_ID);
+ return attributeDescriptor;
+ }
+
+ private List getComponents(JsonParser parser) throws IOException {
+ List dataAttributes = new ArrayList<>();
+ while (!JsonToken.END_ARRAY.equals(parser.nextToken())) {
+ if (JsonToken.START_OBJECT.equals(parser.currentToken())) {
+ DataAttributeImpl dataAttribute = new DataAttributeImpl();
+ getComponent(parser, dataAttribute);
+ dataAttributes.add(dataAttribute);
+ }
+ }
+ return dataAttributes;
+ }
+
+ private void getComponent(JsonParser parser, DataAttributeImpl dataAttribute) throws IOException {
+ dataAttribute.setMaxOccurs(1);
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.CONCEPT_IDENTITY:
+ String conceptIdentity = getStringJsonField(parser);
+ if (conceptIdentity != null) {
+ dataAttribute.setConceptIdentity(new IdentifiableArtefactReferenceImpl(conceptIdentity));
+ }
+ break;
+ case StructureUtils.IS_MANDATORY:
+ boolean isMandatory = ReaderUtils.getBooleanJsonField(parser);
+ if (isMandatory) {
+ dataAttribute.setMinOccurs(1);
+ } else {
+ dataAttribute.setMinOccurs(0);
+ }
+ break;
+ case StructureUtils.CONCEPT_ROLES:
+ List conceptRoles = ConceptRoleUtils.getConceptRoles(parser);
+ if (CollectionUtils.isNotEmpty(conceptRoles)) {
+ dataAttribute.setConceptRoles(conceptRoles);
+ }
+ break;
+ case StructureUtils.ATTRIBUTE_RELATIONSHIP:
+ dataAttribute.setAttributeRelationship(getAttributeRelationShip(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.MEASURE_RELATIONSHIP:
+ dataAttribute.setMeasureRelationship(getMeasureRelationship(parser));
+ break;
+ case StructureUtils.LOCAL_REPRESENTATION:
+ dataAttribute.setLocalRepresentation(getRepresentation(parser));
+ break;
+ default:
+ identifiableReader.read(dataAttribute, parser);
+ break;
+ }
+ }
+ }
+
+ private MeasureRelationship getMeasureRelationship(JsonParser parser) throws IOException {
+ MeasureRelationshipImpl measureRelationship = new MeasureRelationshipImpl();
+ measureRelationship.setMeasures(ReaderUtils.getListStrings(parser));
+ return measureRelationship;
+ }
+
+ private Representation getRepresentation(JsonParser parser) throws IOException {
+ RepresentationReader representationReader = new RepresentationReader();
+ return representationReader.getRepresentations(parser);
+ }
+
+ private MetadataAttributeRefImpl getMetadataAttributeRef(JsonParser parser) {
+ if (JsonToken.VALUE_NULL.equals(parser.currentToken())) {
+ return null;
+ }
+ try {
+ MetadataAttributeRefImpl metadataAttributeRef = new MetadataAttributeRefImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.METADATA_ATTRIBUTE_REFERENCE:
+ String attributeId = getStringJsonField(parser);
+ if (attributeId != null) {
+ metadataAttributeRef.setId(attributeId);
+ }
+ break;
+ case StructureUtils.ATTRIBUTE_RELATIONSHIP:
+ metadataAttributeRef.setMetadataRelationship(getAttributeRelationShip(parser));
+ parser.nextToken();
+ break;
+ default:
+ identifiableReader.read(metadataAttributeRef, parser);
+ }
+ }
+ return metadataAttributeRef;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private AttributeRelationship getAttributeRelationShip(JsonParser parser) throws IOException {
+ parser.nextToken();
+ if (isNullValue(parser)) {
+ return null;
+ }
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.currentName();
+ switch (fieldName) {
+ case StructureUtils.DIMENSIONS:
+ DimensionRelationshipImpl dimensionRelationship = new DimensionRelationshipImpl();
+ dimensionRelationship.setDimensions(ReaderUtils.getListStrings(parser));
+ return dimensionRelationship;
+ case StructureUtils.OBSERVATION:
+ parser.nextToken();
+ parser.nextToken();
+ return new ObservationRelationshipImpl();
+ case StructureUtils.GROUP:
+ GroupRelationshipImpl groupRelationship = new GroupRelationshipImpl();
+ groupRelationship.setGroupKey(getStringJsonField(parser));
+ return groupRelationship;
+ case StructureUtils.NONE:
+ parser.nextToken();
+ parser.nextToken();
+ return null;
+ default:
+ throw new IllegalArgumentException("no such AttributeRelationship: " + fieldName);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorisationReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorisationReader.java
new file mode 100644
index 0000000..c9eb301
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorisationReader.java
@@ -0,0 +1,68 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.CategorisationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactReference;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.lang3.StringUtils;
+
+public class CategorisationReader extends MaintainableReader {
+
+ public CategorisationReader(VersionableReader versionableArtefact) {
+ super(versionableArtefact);
+ }
+
+ @Override
+ protected CategorisationImpl createMaintainableArtefact() {
+ return new CategorisationImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, CategorisationImpl categorisation) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.TARGET:
+ getTarget(parser).ifPresent(categorisation::setCategorizedBy);
+ break;
+ case StructureUtils.SOURCE:
+ getSource(parser).ifPresent(categorisation::setCategorizedArtefact);
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "Categorisation: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.CATEGORISATIONS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getCategorisations().addAll(artefacts);
+ }
+
+ private Optional getTarget(JsonParser parser) throws IOException {
+ String target = ReaderUtils.getStringJsonField(parser);
+ if (StringUtils.isBlank(target)) {
+ return Optional.empty();
+ }
+ return Optional.of(new IdentifiableArtefactReferenceImpl(target));
+ }
+
+ private Optional getSource(JsonParser parser) throws IOException {
+ String source = ReaderUtils.getStringJsonField(parser);
+ if (StringUtils.isBlank(source)) {
+ return Optional.empty();
+ }
+ return Optional.of(new IdentifiableArtefactReferenceImpl(source));
+ }
+
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorySchemeMapReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorySchemeMapReader.java
new file mode 100644
index 0000000..1896e75
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorySchemeMapReader.java
@@ -0,0 +1,56 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.CategorySchemeMapImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ItemMap;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class CategorySchemeMapReader extends MaintainableReader implements ItemMapReader {
+
+ public CategorySchemeMapReader(VersionableReader versionableArtefact) {
+ super(versionableArtefact);
+ }
+
+ @Override
+ protected CategorySchemeMapImpl createMaintainableArtefact() {
+ return new CategorySchemeMapImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, CategorySchemeMapImpl categorySchemeMap) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.TARGET:
+ getTargetSource(parser).ifPresent(categorySchemeMap::setTarget);
+ break;
+ case StructureUtils.SOURCE:
+ getTargetSource(parser).ifPresent(categorySchemeMap::setSource);
+ break;
+ case StructureUtils.ITEM_MAPS:
+ List itemMaps = ReaderUtils.getArray(parser, (this::getItemMap));
+ if (CollectionUtils.isNotEmpty(itemMaps)) {
+ categorySchemeMap.setItemMaps(itemMaps);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "CategorySchemeMap: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.CATEGORY_SCHEME_MAPS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getCategorySchemeMaps().addAll(artefacts);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorySchemeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorySchemeReader.java
new file mode 100644
index 0000000..c89efb3
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CategorySchemeReader.java
@@ -0,0 +1,80 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getBooleanJsonField;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Category;
+import com.epam.jsdmx.infomodel.sdmx30.CategoryImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CategorySchemeImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class CategorySchemeReader extends MaintainableReader {
+
+ private final NameableReader nameableArtefact;
+
+ public CategorySchemeReader(VersionableReader versionableArtefact, NameableReader nameableArtefact) {
+ super(versionableArtefact);
+ this.nameableArtefact = nameableArtefact;
+ }
+
+ @Override
+ protected CategorySchemeImpl createMaintainableArtefact() {
+ return new CategorySchemeImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, CategorySchemeImpl categoryScheme) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.IS_PARTIAL:
+ categoryScheme.setPartial(getBooleanJsonField(parser));
+ break;
+ case StructureUtils.CATEGORIES:
+ List categories = ReaderUtils.getArray(parser, (this::getCategory));
+ if (CollectionUtils.isNotEmpty(categories)) {
+ categoryScheme.setItems(categories);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "CategoryScheme: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.CATEGORY_SCHEMES;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getCategorySchemes().addAll(artefacts);
+ }
+
+ private Category getCategory(JsonParser parser) {
+ try {
+ CategoryImpl category = new CategoryImpl();
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ if (StructureUtils.CATEGORIES.equals(fieldName)) {
+ parser.nextToken();
+ category.setHierarchy(ReaderUtils.getArray(parser, (this::getCategory)));
+ } else {
+ nameableArtefact.read(category, parser);
+ }
+ }
+ return category;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodeImplReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodeImplReader.java
new file mode 100644
index 0000000..e84fdbf
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodeImplReader.java
@@ -0,0 +1,6 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import com.epam.jsdmx.infomodel.sdmx30.CodeImpl;
+
+public class CodeImplReader implements CodeReader {
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodeReader.java
new file mode 100644
index 0000000..a0102ef
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodeReader.java
@@ -0,0 +1,49 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+
+import java.io.IOException;
+import java.util.Map;
+
+import com.epam.jsdmx.infomodel.sdmx30.CodeImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+public interface CodeReader {
+
+ default T getCode(JsonParser parser,
+ T code,
+ NameableReader nameableReader,
+ Map codeWithParentId) throws IOException {
+ String parentId = null;
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.PARENT:
+ parser.nextToken();
+ parentId = parser.getText();
+ code.setParentId(parentId);
+ break;
+ case StructureUtils.ID:
+ case StructureUtils.ANNOTATIONS:
+ case StructureUtils.NAMES:
+ case StructureUtils.DESCRIPTIONS:
+ case StructureUtils.DESCRIPTION:
+ case StructureUtils.NAME:
+ nameableReader.read(code, parser);
+ break;
+ default:
+ readExtendedCodeFields(parser, code);
+ break;
+ }
+ }
+ codeWithParentId.put(code, parentId);
+ return code;
+ }
+
+ default void readExtendedCodeFields(JsonParser parser, T code) throws IOException {
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodelistExtensionReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodelistExtensionReader.java
new file mode 100644
index 0000000..6550b34
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodelistExtensionReader.java
@@ -0,0 +1,188 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.epam.jsdmx.infomodel.sdmx30.BaseCodeSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CascadeValue;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistExtension;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistExtensionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ExclusiveCodeSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.InclusiveCodeSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.MemberValue;
+import com.epam.jsdmx.infomodel.sdmx30.MemberValueImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class CodelistExtensionReader {
+
+ public List getCodeListExtensions(JsonParser parser) throws IOException {
+ List codelistExtensions = new ArrayList<>();
+ int sequence = 0;
+ while (!JsonToken.END_ARRAY.equals(parser.nextToken())) {
+ if (JsonToken.START_OBJECT.equals(parser.currentToken())) {
+ CodelistExtensionImpl codelistExtension = getCodelistExtension(parser, sequence);
+ if (codelistExtension != null) {
+ codelistExtensions.add(codelistExtension);
+ }
+ sequence++;
+ }
+ }
+ return codelistExtensions;
+ }
+
+ private CodelistExtensionImpl getCodelistExtension(JsonParser parser, int sequence) throws IOException {
+ CodelistExtensionImpl codelistExtension = new CodelistExtensionImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ if (!checkIsNotEmptyObjectAndSkipUntilFieldName(parser)) {
+ return null;
+ }
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.PREFIX:
+ parser.nextToken();
+ String prefix = parser.getValueAsString();
+ codelistExtension.setPrefix(prefix);
+ break;
+ case StructureUtils.SEQUENCE:
+ codelistExtension.setSequence(sequence);
+ break;
+ case StructureUtils.CODE_LIST:
+ parser.nextToken();
+ String codelist = parser.getValueAsString();
+ if (codelist != null) {
+ codelistExtension.setCodelist(new MaintainableArtefactReference(codelist));
+ }
+ break;
+ case StructureUtils.EXCLUSIVE_CODE_SELECTION:
+ parser.nextToken();
+ ExclusiveCodeSelectionImpl exclusiveCodeSelection = new ExclusiveCodeSelectionImpl();
+ setCodeSelection(parser, exclusiveCodeSelection);
+ codelistExtension.setCodeSelection(exclusiveCodeSelection);
+ break;
+ case StructureUtils.INCLUSIVE_CODE_SELECTION:
+ parser.nextToken();
+ InclusiveCodeSelectionImpl inclusiveCodeSelection = new InclusiveCodeSelectionImpl();
+ setCodeSelection(parser, inclusiveCodeSelection);
+ codelistExtension.setCodeSelection(inclusiveCodeSelection);
+ break;
+ default:
+ parser.nextToken();
+ break;
+ }
+ }
+ return codelistExtension;
+ }
+
+ private void setCodeSelection(JsonParser parser, BaseCodeSelectionImpl selection) throws IOException {
+ boolean isWildcardedValues = false;
+ boolean isMemberValues = false;
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ if (!checkIsNotEmptyObjectAndSkipUntilFieldName(parser)) {
+ return;
+ }
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.MEMBER_VALUES:
+ isMemberValues = true;
+ readMemberValues(parser, selection, isWildcardedValues);
+ break;
+ case StructureUtils.WILDCARDED_MEMBER_VALUES:
+ isWildcardedValues = true;
+ readWildcardedValues(parser, selection, isMemberValues);
+ break;
+ default:
+ throw new IllegalArgumentException("no such argument in Code selection: " + fieldName);
+ }
+ }
+ }
+
+ private void readWildcardedValues(JsonParser parser, BaseCodeSelectionImpl selection, boolean isMemberValues) throws IOException {
+ parser.nextToken();
+ if (isMemberValues) {
+ throw new IllegalArgumentException("Cannot specify both wildcarded and explicit selections");
+ }
+ List wildcardedMembers = getWildcardedMembers(parser);
+ if (CollectionUtils.isNotEmpty(wildcardedMembers)) {
+ selection.setMembers(wildcardedMembers);
+ }
+ }
+
+ private void readMemberValues(JsonParser parser, BaseCodeSelectionImpl selection, boolean isWildcardedValues) throws IOException {
+ parser.nextToken();
+ if (isWildcardedValues) {
+ throw new IllegalArgumentException("Cannot specify both wildcarded and explicit selections");
+ }
+ List memberValues = ReaderUtils.getArray(parser, this::getMemberValue);
+ if (CollectionUtils.isNotEmpty(memberValues)) {
+ selection.setMembers(memberValues);
+ }
+ parser.nextToken();
+ }
+
+ private List getWildcardedMembers(JsonParser parser) throws IOException {
+ List stringList = ReaderUtils.getListStrings(parser);
+ return stringList.stream()
+ .filter(Objects::nonNull)
+ .map(str -> {
+ MemberValueImpl memberValue = new MemberValueImpl();
+ memberValue.setValue(str);
+ return memberValue;
+ })
+ .collect(Collectors.toList());
+ }
+
+ private MemberValue getMemberValue(JsonParser parser) {
+ try {
+ MemberValueImpl memberValue = new MemberValueImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ if (!checkIsNotEmptyObjectAndSkipUntilFieldName(parser)) {
+ return null;
+ }
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.VALUE:
+ parser.nextToken();
+ String value = parser.getText();
+ memberValue.setValue(value);
+ break;
+ case StructureUtils.CASCADE_VALUES:
+ parser.nextToken();
+ String cascadeSelection = parser.getText();
+ CascadeValue cascadeValue;
+ switch (cascadeSelection) {
+ case StructureUtils.TRUE:
+ cascadeValue = CascadeValue.TRUE;
+ break;
+ case StructureUtils.FALSE:
+ cascadeValue = CascadeValue.FALSE;
+ break;
+ case StructureUtils.EXCLUDE_ROOT:
+ cascadeValue = CascadeValue.EXCLUDE_ROOT;
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected enum constant: " + cascadeSelection);
+ }
+ memberValue.setCascadeValue(cascadeValue);
+ break;
+ default:
+ // unrecognized field, skip it
+ parser.nextToken();
+ break;
+ }
+ }
+ return memberValue;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodelistReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodelistReader.java
new file mode 100644
index 0000000..59faa40
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CodelistReader.java
@@ -0,0 +1,88 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.CodeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistExtension;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class CodelistReader extends MaintainableReader {
+
+ private final NameableReader nameableReader;
+ private final CodelistExtensionReader codelistExtensionReader;
+ private final CodeImplReader codeImplReader;
+ Map codeWithParentId = new HashMap<>();
+
+ public CodelistReader(VersionableReader versionableArtefact,
+ NameableReader nameableArtefact,
+ CodelistExtensionReader codelistExtensionReader,
+ CodeImplReader codeImplReader) {
+ super(versionableArtefact);
+ this.nameableReader = nameableArtefact;
+ this.codelistExtensionReader = codelistExtensionReader;
+ this.codeImplReader = codeImplReader;
+ }
+
+ @Override
+ protected CodelistImpl createMaintainableArtefact() {
+ return new CodelistImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, CodelistImpl codeList) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.CODES:
+ List codeImpls = ReaderUtils.getArray(parser, this::getCodeImpl);
+ if (CollectionUtils.isNotEmpty(codeImpls)) {
+ codeList.setItems(List.copyOf(codeImpls));
+ }
+ break;
+ case StructureUtils.CODE_LIST_EXTENSIONS:
+ parser.nextToken();
+ List codeListExtensions = codelistExtensionReader.getCodeListExtensions(parser);
+ if (CollectionUtils.isNotEmpty(codeListExtensions)) {
+ codeList.setExtensions(codeListExtensions);
+ }
+ break;
+ case StructureUtils.IS_PARTIAL:
+ boolean isPartial = ReaderUtils.getBooleanJsonField(parser);
+ codeList.setPartial(isPartial);
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "CodeList: " + fieldName);
+ }
+ }
+
+ private CodeImpl getCodeImpl(JsonParser parser) {
+ try {
+ return codeImplReader.getCode(parser, new CodeImpl(), nameableReader, codeWithParentId);
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.CODE_LISTS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getCodelists().addAll(artefacts);
+ }
+
+ @Override
+ protected void clean() {
+ codeWithParentId.clear();
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptRoleUtils.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptRoleUtils.java
new file mode 100644
index 0000000..ba8b29c
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptRoleUtils.java
@@ -0,0 +1,33 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+
+import com.fasterxml.jackson.core.JsonParser;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class ConceptRoleUtils {
+
+ public static List getConceptRoles(JsonParser parser) throws IOException {
+ List identifiableArtefactReferences = new ArrayList<>();
+ List conceptRolesStrings = ReaderUtils.getListStrings(parser);
+ if (conceptRolesStrings.isEmpty()) {
+ return Collections.emptyList();
+ }
+ for (String conceptRole : conceptRolesStrings) {
+ if (conceptRole != null) {
+ IdentifiableArtefactReferenceImpl identifiableArtefactReference =
+ new IdentifiableArtefactReferenceImpl(conceptRole);
+ identifiableArtefactReferences.add(identifiableArtefactReference);
+ }
+ }
+ return identifiableArtefactReferences;
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptSchemeMapReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptSchemeMapReader.java
new file mode 100644
index 0000000..f44fb04
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptSchemeMapReader.java
@@ -0,0 +1,56 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptSchemeMapImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ItemMap;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class ConceptSchemeMapReader extends MaintainableReader implements ItemMapReader {
+
+ public ConceptSchemeMapReader(VersionableReader versionableArtefact) {
+ super(versionableArtefact);
+ }
+
+ @Override
+ protected ConceptSchemeMapImpl createMaintainableArtefact() {
+ return new ConceptSchemeMapImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, ConceptSchemeMapImpl conceptSchemeMap) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.TARGET:
+ getTargetSource(parser).ifPresent(conceptSchemeMap::setTarget);
+ break;
+ case StructureUtils.SOURCE:
+ getTargetSource(parser).ifPresent(conceptSchemeMap::setSource);
+ break;
+ case StructureUtils.ITEM_MAPS:
+ List itemMaps = ReaderUtils.getArray(parser, (this::getItemMap));
+ if (CollectionUtils.isNotEmpty(itemMaps)) {
+ conceptSchemeMap.setItemMaps(itemMaps);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "ConceptSchemeMap: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.CONCEPT_SCHEME_MAPS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getConceptSchemeMaps().addAll(artefacts);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptSchemeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptSchemeReader.java
new file mode 100644
index 0000000..693565b
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ConceptSchemeReader.java
@@ -0,0 +1,127 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.Concept;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptImpl;
+import com.epam.jsdmx.infomodel.sdmx30.ConceptSchemeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IsoConceptReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Representation;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class ConceptSchemeReader extends MaintainableReader {
+ private final NameableReader nameableReader;
+
+ public ConceptSchemeReader(VersionableReader versionableArtefact, NameableReader nameableReader) {
+ super(versionableArtefact);
+ this.nameableReader = nameableReader;
+ }
+
+ @Override
+ protected ConceptSchemeImpl createMaintainableArtefact() {
+ return new ConceptSchemeImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, ConceptSchemeImpl conceptScheme) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.IS_PARTIAL:
+ conceptScheme.setPartial(ReaderUtils.getBooleanJsonField(parser));
+ break;
+ case StructureUtils.CONCEPTS:
+ List concepts = ReaderUtils.getArray(parser, this::getConcept);
+ if (CollectionUtils.isNotEmpty(concepts)) {
+ conceptScheme.setItems(concepts);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "ConceptScheme: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.CONCEPT_SCHEMES;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getConceptSchemes().addAll(artefacts);
+ }
+
+ private Concept getConcept(JsonParser parser) {
+ try {
+ ConceptImpl concept = new ConceptImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.ISOCONCEPTREFERENCE:
+ concept.setIsoConceptReference(getIsoConceptReference(parser));
+ break;
+ case StructureUtils.CORE_REPRESENTATION:
+ RepresentationReader reader = new RepresentationReader();
+ Representation representations = reader.getRepresentations(parser);
+ if (representations != null) {
+ concept.setCoreRepresentation(representations);
+ }
+ break;
+ default:
+ nameableReader.read(concept, parser);
+ break;
+ }
+ }
+ return concept;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private IsoConceptReferenceImpl getIsoConceptReference(JsonParser parser) throws IOException {
+ parser.nextToken();
+ IsoConceptReferenceImpl isoConceptReference = new IsoConceptReferenceImpl();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return null;
+ }
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.CONCEPT_SCHEME_ID:
+ String conceptSchemeId = ReaderUtils.getStringJsonField(parser);
+ if (conceptSchemeId != null) {
+ isoConceptReference.setSchemeId(conceptSchemeId);
+ }
+ parser.nextToken();
+ break;
+ case StructureUtils.CONCEPT_ID:
+ String conceptId = ReaderUtils.getStringJsonField(parser);
+ if (conceptId != null) {
+ isoConceptReference.setConceptId(conceptId);
+ }
+ parser.nextToken();
+ break;
+ case StructureUtils.CONCEPT_AGENCY:
+ String conceptAgency = ReaderUtils.getStringJsonField(parser);
+ if (conceptAgency != null) {
+ isoConceptReference.setAgency(conceptAgency);
+ }
+ parser.nextToken();
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "IsoConceptReference: " + fieldName);
+ }
+ }
+ return isoConceptReference;
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ContactReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ContactReader.java
new file mode 100644
index 0000000..5ee7e32
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ContactReader.java
@@ -0,0 +1,109 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getLocalizedField;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Map;
+
+import com.epam.jsdmx.infomodel.sdmx30.Contact;
+import com.epam.jsdmx.infomodel.sdmx30.ContactImpl;
+import com.epam.jsdmx.infomodel.sdmx30.InternationalString;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class ContactReader {
+
+ public Contact getContact(JsonParser parser) {
+ try {
+ ContactImpl contact = new ContactImpl();
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.NAME:
+ String name = ReaderUtils.getFieldAsString(parser);
+ contact.setName(name);
+ break;
+ case StructureUtils.DEPARTMENT:
+ String department = ReaderUtils.getFieldAsString(parser);
+ contact.setOrganizationUnit(department);
+ break;
+ case StructureUtils.ROLE:
+ Map roles = getLocalizedField(parser);
+ setRoles(contact, roles);
+ break;
+ case StructureUtils.TELEPHONES:
+ List telephones = ReaderUtils.getListStrings(parser);
+ setTelephones(contact, telephones);
+ break;
+ case StructureUtils.FAXES:
+ List faxes = ReaderUtils.getListStrings(parser);
+ setFaxes(contact, faxes);
+ break;
+ case StructureUtils.X_400_S:
+ List x400 = ReaderUtils.getListStrings(parser);
+ setX400(contact, x400);
+ break;
+ case StructureUtils.EMAILS:
+ List emails = ReaderUtils.getListStrings(parser);
+ setEmail(contact, emails);
+ break;
+ case StructureUtils.URIS:
+ List uris = ReaderUtils.getListStrings(parser);
+ setURI(contact, uris);
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "Contacts: " + fieldName);
+ }
+ }
+ return contact;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void setRoles(ContactImpl contact, Map roles) {
+ if (roles != null) {
+ contact.setResponsibility(new InternationalString(roles));
+ }
+ }
+
+ private void setTelephones(ContactImpl contact, List telephones) {
+ if (CollectionUtils.isNotEmpty(telephones)) {
+ contact.setTelephone(telephones.get(0));
+ }
+ }
+
+ private void setFaxes(ContactImpl contact, List faxes) {
+ if (CollectionUtils.isNotEmpty(faxes)) {
+ contact.setFax(faxes.get(0));
+ }
+ }
+
+ private void setX400(ContactImpl contact, List x400) {
+ if (CollectionUtils.isNotEmpty(x400)) {
+ contact.setX400(x400.get(0));
+ }
+ }
+
+ private void setEmail(ContactImpl contact, List emails) {
+ if (CollectionUtils.isNotEmpty(emails)) {
+ contact.setEmail(emails.get(0));
+ }
+ }
+
+ private void setURI(ContactImpl contact, List uris) throws URISyntaxException {
+ if (CollectionUtils.isNotEmpty(uris) && uris.get(0) != null) {
+ contact.setUri(new URI(uris.get(0)));
+ }
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CubeRegionReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CubeRegionReader.java
new file mode 100644
index 0000000..b7ab016
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/CubeRegionReader.java
@@ -0,0 +1,133 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegion;
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegionKey;
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegionKeyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelection;
+import com.epam.jsdmx.infomodel.sdmx30.SelectionValue;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class CubeRegionReader {
+
+ private final MemberSelectionReader memberSelectionReader;
+ private final AnnotableReader annotableReader;
+
+ public CubeRegionReader(MemberSelectionReader memberSelectionReader, AnnotableReader annotableReader) {
+ this.memberSelectionReader = memberSelectionReader;
+ this.annotableReader = annotableReader;
+ }
+
+ public CubeRegion getCubeRegion(JsonParser parser) {
+ try {
+ CubeRegionImpl cubeRegion = new CubeRegionImpl();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return null;
+ }
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.INCLUDE:
+ boolean include = ReaderUtils.getBooleanJsonField(parser);
+ cubeRegion.setIncluded(include);
+ parser.nextToken();
+ break;
+ case StructureUtils.COMPONENTS:
+ List memberSelections = ReaderUtils.getArray(parser, (this::getMemberSelection));
+ if (CollectionUtils.isNotEmpty(memberSelections)) {
+ cubeRegion.setMemberSelections(memberSelections);
+ }
+ parser.nextToken();
+ break;
+ case StructureUtils.KEY_VALUES:
+ List cubeRegionKeys = ReaderUtils.getArray(parser, (this::getKeyValue));
+ if (CollectionUtils.isNotEmpty(cubeRegionKeys)) {
+ cubeRegion.setCubeRegionKeys(cubeRegionKeys);
+ }
+ parser.nextToken();
+ break;
+ default:
+ annotableReader.read(cubeRegion, parser);
+ parser.nextToken();
+ break;
+ }
+ }
+ return cubeRegion;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private MemberSelection getMemberSelection(JsonParser parser) {
+ return memberSelectionReader.getMemberSelection(parser);
+ }
+
+ public CubeRegionKey getKeyValue(JsonParser parser) {
+ try {
+ CubeRegionKeyImpl cubeRegionKey = new CubeRegionKeyImpl();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return null;
+ }
+ List selectionValues = new ArrayList<>();
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.ID:
+ cubeRegionKey.setComponentId(ReaderUtils.getFieldAsString(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.VALID_FROM:
+ cubeRegionKey.setValidFrom(ReaderUtils.getInstantObj(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.VALID_TO:
+ cubeRegionKey.setValidTo(ReaderUtils.getInstantObj(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.INCLUDE:
+ boolean include = ReaderUtils.getBooleanJsonField(parser);
+ cubeRegionKey.setIncluded(include);
+ parser.nextToken();
+ break;
+ case StructureUtils.REMOVE_PREFIX:
+ boolean removePrefix = ReaderUtils.getBooleanJsonField(parser);
+ cubeRegionKey.setRemovePrefix(removePrefix);
+ parser.nextToken();
+ break;
+ case StructureUtils.TIME_RANGE:
+ List timeRanges = memberSelectionReader.getTimeRanges(parser);
+ if (CollectionUtils.isNotEmpty(timeRanges)) {
+ selectionValues.addAll(timeRanges);
+ }
+ parser.nextToken();
+ break;
+ case StructureUtils.VALUES:
+ List values = ReaderUtils.getArray(parser, (memberSelectionReader::getMemberValue));
+ selectionValues.addAll(values);
+ parser.nextToken();
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + " CubeRegionKey: " + fieldName);
+ }
+ }
+ cubeRegionKey.setSelectionValues(selectionValues);
+ return cubeRegionKey;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataConstraintReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataConstraintReader.java
new file mode 100644
index 0000000..082c611
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataConstraintReader.java
@@ -0,0 +1,124 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getStringJsonField;
+import static com.epam.jsdmx.json20.structure.writer.StructureUtils.STRING_CONSTRAINT_ROLE;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.CubeRegion;
+import com.epam.jsdmx.infomodel.sdmx30.DataConstraintImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeySet;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class DataConstraintReader extends MaintainableReader {
+
+ private final ReleaseCalendarReader releaseCalendarReader;
+ private final DataKeySetReader dataKeySetReader;
+ private final CubeRegionReader cubeRegionReader;
+
+ public DataConstraintReader(VersionableReader versionableArtefact,
+ ReleaseCalendarReader releaseCalendarReader,
+ DataKeySetReader dataKeySetReader,
+ CubeRegionReader cubeRegionReader) {
+ super(versionableArtefact);
+ this.releaseCalendarReader = releaseCalendarReader;
+ this.dataKeySetReader = dataKeySetReader;
+ this.cubeRegionReader = cubeRegionReader;
+ }
+
+ @Override
+ protected DataConstraintImpl createMaintainableArtefact() {
+ return new DataConstraintImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, DataConstraintImpl dataConstraint) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.ROLE:
+ dataConstraint.setConstraintRoleType(STRING_CONSTRAINT_ROLE.get(ReaderUtils.getFieldAsString(parser)));
+ break;
+ case StructureUtils.RELEASE_CALENDAR:
+ dataConstraint.setReleaseCalendar(releaseCalendarReader.getReleaseCalendar(parser));
+ break;
+ case StructureUtils.CONSTRAINT_ATTACHMENT:
+ dataConstraint.setConstrainedArtefacts(getConstraintAttachment(parser));
+ break;
+ case StructureUtils.DATA_KEY_SETS:
+ List dataKeySets = ReaderUtils.getArray(parser, (dataKeySetReader::getDataKeySet));
+ if (CollectionUtils.isNotEmpty(dataKeySets)) {
+ dataConstraint.setDataContentKeys(dataKeySets);
+ }
+ break;
+ case StructureUtils.CUBE_REGIONS:
+ List cubeRegions = ReaderUtils.getArray(parser, (cubeRegionReader::getCubeRegion));
+ if (CollectionUtils.isNotEmpty(cubeRegions)) {
+ dataConstraint.setCubeRegions(cubeRegions);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "DataConstraint: " + fieldName);
+ }
+ }
+
+ private List getConstraintAttachment(JsonParser parser) throws IOException {
+ parser.nextToken();
+ List artefactReferences = new ArrayList<>();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return Collections.emptyList();
+ }
+ while (parser.currentToken() != JsonToken.END_OBJECT) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.DATA_PROVIDER:
+ String metadataProvider = getStringJsonField(parser);
+ if (metadataProvider != null) {
+ artefactReferences.add(new IdentifiableArtefactReferenceImpl(metadataProvider));
+ }
+ parser.nextToken();
+ break;
+ case StructureUtils.DATA_STRUCTURES:
+ case StructureUtils.DATAFLOWS:
+ case StructureUtils.PROVISION_AGREEMENTS:
+ List metadataSets = ReaderUtils.getListStrings(parser);
+ List references = metadataSets.stream()
+ .filter(Objects::nonNull)
+ .map(IdentifiableArtefactReferenceImpl::new)
+ .collect(Collectors.toList());
+ artefactReferences.addAll(references);
+ parser.nextToken();
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + " ConstraintAttachment: " + fieldName);
+ }
+ }
+ return artefactReferences;
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.DATA_CONSTRAINTS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getDataConstraints().addAll(artefacts);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataConsumerSchemeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataConsumerSchemeReader.java
new file mode 100644
index 0000000..6210b92
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataConsumerSchemeReader.java
@@ -0,0 +1,77 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.DataConsumer;
+import com.epam.jsdmx.infomodel.sdmx30.DataConsumerImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataConsumerSchemeImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class DataConsumerSchemeReader extends MaintainableReader {
+
+ private final OrganisationReader organisationReader;
+ private final Map dataConsumerWithParent = new HashMap<>();
+
+ public DataConsumerSchemeReader(VersionableReader versionableArtefact,
+ OrganisationReader organisationReader) {
+ super(versionableArtefact);
+ this.organisationReader = organisationReader;
+ }
+
+ @Override
+ protected DataConsumerSchemeImpl createMaintainableArtefact() {
+ return new DataConsumerSchemeImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, DataConsumerSchemeImpl dataConsumerScheme) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.IS_PARTIAL:
+ dataConsumerScheme.setPartial(ReaderUtils.getBooleanJsonField(parser));
+ break;
+ case StructureUtils.DATA_CONSUMERS:
+ List dataConsumers = ReaderUtils.getArray(parser, (this::getDataConsumer));
+ if (CollectionUtils.isNotEmpty(dataConsumers)) {
+ Map> dataConsumerListMap = organisationReader.formHierarchy(dataConsumerWithParent);
+ for (Map.Entry> consumersHier : dataConsumerListMap.entrySet()) {
+ List value = consumersHier.getValue();
+ List hierarchy = new ArrayList<>(value);
+ consumersHier.getKey().setHierarchy(hierarchy);
+ }
+ dataConsumerScheme.setItems(dataConsumers);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "DataConsumerScheme: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.DATA_CONSUMER_SCHEMES;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getDataConsumerSchemes().addAll(artefacts);
+ }
+
+ private DataConsumer getDataConsumer(JsonParser parser) {
+ return organisationReader.getOrganisation(parser, new DataConsumerImpl(), dataConsumerWithParent);
+ }
+
+ @Override
+ protected void clean() {
+ dataConsumerWithParent.clear();
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataFlowReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataFlowReader.java
new file mode 100644
index 0000000..1b35da1
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataFlowReader.java
@@ -0,0 +1,44 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.DataflowImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactReference;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+public class DataFlowReader extends MaintainableReader {
+
+ public DataFlowReader(VersionableReader versionableArtefact) {
+ super(versionableArtefact);
+ }
+
+ @Override
+ protected DataflowImpl createMaintainableArtefact() {
+ return new DataflowImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, DataflowImpl dataflow) throws IOException {
+ String fieldName = parser.getCurrentName();
+ if (StructureUtils.STRUCTURE.equals(fieldName)) {
+ String urn = ReaderUtils.getStringJsonField(parser);
+ if (urn != null) {
+ dataflow.setStructure(new MaintainableArtefactReference(urn));
+ }
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.DATAFLOWS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getDataflows().addAll(artefacts);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataKeySetReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataKeySetReader.java
new file mode 100644
index 0000000..cd175b3
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataKeySetReader.java
@@ -0,0 +1,164 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.ComponentValue;
+import com.epam.jsdmx.infomodel.sdmx30.ComponentValueImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataKey;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeySet;
+import com.epam.jsdmx.infomodel.sdmx30.DataKeySetImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelection;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class DataKeySetReader {
+
+ private final AnnotableReader annotableReader;
+ private final MemberSelectionReader memberSelectionReader;
+
+ public DataKeySetReader(AnnotableReader annotableReader,
+ MemberSelectionReader memberSelectionReader) {
+ this.annotableReader = annotableReader;
+ this.memberSelectionReader = memberSelectionReader;
+ }
+
+ public DataKeySet getDataKeySet(JsonParser parser) {
+ try {
+ parser.nextToken();
+ DataKeySetImpl dataKeySet = new DataKeySetImpl();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return null;
+ }
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.IS_INCLUDED:
+ dataKeySet.setIncluded(ReaderUtils.getBooleanJsonField(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.KEYS:
+ List dataKeys = ReaderUtils.getArray(parser, this::getKey);
+ if (CollectionUtils.isNotEmpty(dataKeys)) {
+ dataKeySet.setKeys(dataKeys);
+ }
+ parser.nextToken();
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + " DataKeySet: " + fieldName);
+ }
+ }
+ return dataKeySet;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private DataKey getKey(JsonParser parser) {
+ try {
+ parser.nextToken();
+ DataKeyImpl dataKey = new DataKeyImpl();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return null;
+ }
+
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.VALID_FROM:
+ dataKey.setValidFrom(ReaderUtils.getInstantObj(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.VALID_TO:
+ dataKey.setValidTo(ReaderUtils.getInstantObj(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.INCLUDE:
+ boolean include = ReaderUtils.getBooleanJsonField(parser);
+ dataKey.setIncluded(include);
+ parser.nextToken();
+ break;
+ case StructureUtils.COMPONENTS:
+ List memberSelections = ReaderUtils.getArray(parser, (this::getMemberSelection));
+ if (CollectionUtils.isNotEmpty(memberSelections)) {
+ dataKey.setMemberSelections(memberSelections);
+ }
+ parser.nextToken();
+ break;
+ case StructureUtils.KEY_VALUES:
+ List keyValues = ReaderUtils.getArray(parser, (this::getKeyValue));
+ if (CollectionUtils.isNotEmpty(keyValues)) {
+ dataKey.setKeyValues(keyValues);
+ }
+ parser.nextToken();
+ break;
+ default:
+ annotableReader.read(dataKey, parser);
+ parser.nextToken();
+ break;
+ }
+ }
+ return dataKey;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private ComponentValue getKeyValue(JsonParser parser) {
+ try {
+ parser.nextToken();
+ ComponentValueImpl componentValue = new ComponentValueImpl();
+
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return null;
+ }
+
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.ID:
+ String id = ReaderUtils.getStringJsonField(parser);
+ componentValue.setComponentId(id);
+ parser.nextToken();
+ break;
+ case StructureUtils.INCLUDE:
+ boolean include = ReaderUtils.getBooleanJsonField(parser);
+ componentValue.setIncluded(include);
+ parser.nextToken();
+ break;
+ case StructureUtils.REMOVE_PREFIX:
+ boolean removePrefix = ReaderUtils.getBooleanJsonField(parser);
+ componentValue.setRemovePrefix(removePrefix);
+ parser.nextToken();
+ break;
+ case StructureUtils.VALUE:
+ String value = ReaderUtils.getStringJsonField(parser);
+ componentValue.setValue(value);
+ parser.nextToken();
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + " ComponentValue: " + fieldName);
+ }
+ }
+ return componentValue;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private MemberSelection getMemberSelection(JsonParser parser) {
+ return memberSelectionReader.getMemberSelection(parser);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataProviderSchemeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataProviderSchemeReader.java
new file mode 100644
index 0000000..eb5f08b
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataProviderSchemeReader.java
@@ -0,0 +1,77 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.DataProvider;
+import com.epam.jsdmx.infomodel.sdmx30.DataProviderImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataProviderSchemeImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class DataProviderSchemeReader extends MaintainableReader {
+
+ private final OrganisationReader organisationReader;
+ private final Map dataProviderSchemeMap = new HashMap<>();
+
+ public DataProviderSchemeReader(VersionableReader versionableArtefact,
+ OrganisationReader organisationReader) {
+ super(versionableArtefact);
+ this.organisationReader = organisationReader;
+ }
+
+ @Override
+ protected DataProviderSchemeImpl createMaintainableArtefact() {
+ return new DataProviderSchemeImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, DataProviderSchemeImpl dataProviderScheme) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.IS_PARTIAL:
+ dataProviderScheme.setPartial(ReaderUtils.getBooleanJsonField(parser));
+ break;
+ case StructureUtils.DATA_PROVIDERS:
+ List dataProviders = ReaderUtils.getArray(parser, (this::getDataProvider));
+ if (CollectionUtils.isNotEmpty(dataProviders)) {
+ Map> dataProviderListMap = organisationReader.formHierarchy(dataProviderSchemeMap);
+ for (Map.Entry> consumersHier : dataProviderListMap.entrySet()) {
+ List value = consumersHier.getValue();
+ List hierarchy = new ArrayList<>(value);
+ consumersHier.getKey().setHierarchy(hierarchy);
+ }
+ dataProviderScheme.setItems(dataProviders);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "DataProviderScheme: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.DATA_PROVIDER_SCHEMES;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getDataProviderSchemes().addAll(artefacts);
+ }
+
+ private DataProvider getDataProvider(JsonParser parser) {
+ return organisationReader.getOrganisation(parser, new DataProviderImpl(), dataProviderSchemeMap);
+ }
+
+ @Override
+ protected void clean() {
+ dataProviderSchemeMap.clear();
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataReader.java
new file mode 100644
index 0000000..6f95b6f
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataReader.java
@@ -0,0 +1,93 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactsImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefact;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+public class DataReader {
+
+ private final List extends MaintainableReader extends MaintainableArtefact>> readers;
+
+ public DataReader(List extends MaintainableReader extends MaintainableArtefact>> readers) {
+ this.readers = readers;
+ }
+
+ public ArtefactsImpl read(JsonParser parser) throws IOException {
+
+ ArtefactsImpl artefacts = new ArtefactsImpl();
+ if (JsonToken.START_OBJECT.equals(parser.currentToken()) && JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ return null;
+ }
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String currentParserName = parser.getCurrentName();
+ switch (currentParserName) {
+ case StructureUtils.STRUCTURE_MAP:
+ case StructureUtils.CATEGORISATIONS:
+ case StructureUtils.CATEGORY_SCHEMES:
+ case StructureUtils.CODE_LISTS:
+ case StructureUtils.CONCEPT_SCHEMES:
+ case StructureUtils.DATA_STRUCTURES:
+ case StructureUtils.METADATA_STRUCTURES:
+ case StructureUtils.HIERARCHIES:
+ case StructureUtils.REPRESENTATION_MAPS:
+ case StructureUtils.DATAFLOWS:
+ case StructureUtils.METADATAFLOWS:
+ case StructureUtils.AGENCY_SCHEMES:
+ case StructureUtils.CATEGORY_SCHEME_MAPS:
+ case StructureUtils.CONCEPT_SCHEME_MAPS:
+ case StructureUtils.DATA_CONSUMER_SCHEMES:
+ case StructureUtils.DATA_PROVIDER_SCHEMES:
+ case StructureUtils.HIERARCHY_ASSOCIATIONS:
+ case StructureUtils.METADATA_CONSTRAINTS:
+ case StructureUtils.METADATA_PROVIDER_SCHEMES:
+ case StructureUtils.METADATA_PROVISION_AGREEMENTS:
+ case StructureUtils.ORGANISATION_UNIT_SCHEMES:
+ case StructureUtils.ORGANISATION_SCHEME_MAPS:
+ case StructureUtils.PROCESSES:
+ case StructureUtils.PROVISION_AGREEMENTS:
+ case StructureUtils.REPORTING_TAXONOMY_MAPS:
+ case StructureUtils.REPORTING_TAXONOMIES:
+ case StructureUtils.VALUE_LISTS:
+ case StructureUtils.GEOGRAPHIC_CODELISTS:
+ case StructureUtils.GEO_GRID_CODELISTS:
+ case StructureUtils.DATA_CONSTRAINTS:
+ Optional extends MaintainableReader extends MaintainableArtefact>> jsonReader = readers.stream()
+ .filter(read -> currentParserName.equals(read.getName()))
+ .findAny();
+ if (jsonReader.isPresent()) {
+ addArtefacts(parser, jsonReader.get(), artefacts);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "Artefacts: " + currentParserName);
+ }
+ parser.nextToken();
+ }
+ return artefacts;
+ }
+
+ private void addArtefacts(JsonParser parser,
+ MaintainableReader maintainableReader,
+ ArtefactsImpl artefacts) throws IOException {
+ List artefactList = ReaderUtils.getArray(parser, artefactParser -> {
+ try {
+ T art = maintainableReader.read(artefactParser);
+ maintainableReader.clean();
+ return art;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ });
+ maintainableReader.setArtefacts(artefacts, Set.copyOf(artefactList));
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataStructureDefinitionReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataStructureDefinitionReader.java
new file mode 100644
index 0000000..0d035d5
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DataStructureDefinitionReader.java
@@ -0,0 +1,89 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.AttributeDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DataStructureDefinitionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactReference;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+public class DataStructureDefinitionReader extends MaintainableReader {
+
+ private final IdentifiableReader identifiableReader;
+
+ public DataStructureDefinitionReader(VersionableReader versionableArtefact, IdentifiableReader identifiableReader) {
+ super(versionableArtefact);
+ this.identifiableReader = identifiableReader;
+ }
+
+ @Override
+ protected DataStructureDefinitionImpl createMaintainableArtefact() {
+ return new DataStructureDefinitionImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, DataStructureDefinitionImpl dataStructureDefinition) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.METADATA:
+ String metadataUrn = ReaderUtils.getStringJsonField(parser);
+ if (metadataUrn != null) {
+ dataStructureDefinition.setMetadataStructure(new MaintainableArtefactReference(metadataUrn));
+ }
+ break;
+ case StructureUtils.DATA_STRUCTURE_COMPONENTS:
+ getDataStructureComponents(parser, dataStructureDefinition);
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "DataStructureDefinition: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.DATA_STRUCTURES;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getDataStructures().addAll(artefacts);
+ }
+
+ private void getDataStructureComponents(JsonParser parser, DataStructureDefinitionImpl dataStructureDefinition) throws IOException {
+ parser.nextToken();
+ if (ReaderUtils.isNullValue(parser)) {
+ return;
+ }
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.ATTRIBUTE_LIST:
+ AttributeListReader attributeListReader = new AttributeListReader();
+ AttributeDescriptorImpl attributeDescriptor = attributeListReader.getAttributeDescriptor(parser);
+ dataStructureDefinition.setAttributeDescriptor(attributeDescriptor);
+ break;
+ case StructureUtils.DIMENSION_LIST:
+ DimensionListReader dimensionListReader = new DimensionListReader(identifiableReader);
+ dataStructureDefinition.setDimensionDescriptor(dimensionListReader.getDimensionDescriptor(parser));
+ break;
+ case StructureUtils.MEASURE_LIST:
+ MeasureListReader measureListReader = new MeasureListReader();
+ dataStructureDefinition.setMeasureDescriptor(measureListReader.getMeasureDescriptor(parser));
+ break;
+ case StructureUtils.GROUPS:
+ parser.nextToken();
+ parser.skipChildren();
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "DataStructureComponent: " + fieldName);
+ }
+ }
+ }
+
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DimensionListReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DimensionListReader.java
new file mode 100644
index 0000000..994d5d2
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/DimensionListReader.java
@@ -0,0 +1,145 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionComponent;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.DimensionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Representation;
+import com.epam.jsdmx.infomodel.sdmx30.TimeDimensionImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+import com.epam.jsdmx.serializer.util.DimensionUtil;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class DimensionListReader {
+
+ private final IdentifiableReader identifiableReader;
+
+ public DimensionListReader(IdentifiableReader identifiableReader) {
+ this.identifiableReader = identifiableReader;
+ }
+
+ public DimensionDescriptorImpl getDimensionDescriptor(JsonParser parser) throws IOException {
+ parser.nextToken();
+ if (ReaderUtils.isNullValue(parser)) {
+ return null;
+ }
+ DimensionDescriptorImpl dimensionDescriptor = new DimensionDescriptorImpl();
+ List dimensionComponents = new ArrayList<>();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.DIMENSIONS:
+ List dimensions = ReaderUtils.getArray(parser, (this::getDimension));
+ if (CollectionUtils.isNotEmpty(dimensions)) {
+ dimensionComponents.addAll(dimensions);
+ }
+ break;
+ case StructureUtils.TIME_DIMENSIONS:
+ List timeDimensions = ReaderUtils.getArray(parser, (this::getTimeDimension));
+ if (CollectionUtils.isNotEmpty(timeDimensions)) {
+ dimensionComponents.addAll(timeDimensions);
+ }
+ break;
+ default:
+ identifiableReader.read(dimensionDescriptor, parser);
+ break;
+ }
+ }
+
+ DimensionUtil.populateZeroBasedOrder(dimensionComponents);
+
+ dimensionDescriptor.setComponents(dimensionComponents);
+ dimensionDescriptor.setId(StructureUtils.DIMENSION_DESCRIPTOR_ID);
+ return dimensionDescriptor;
+ }
+
+ private TimeDimensionImpl getTimeDimension(JsonParser parser) {
+ try {
+ TimeDimensionImpl timeDimension = new TimeDimensionImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.POSITION:
+ parser.nextToken();
+ timeDimension.setOrder(parser.getIntValue());
+ break;
+ case StructureUtils.TYPE:
+ parser.nextToken();
+ break;
+ case StructureUtils.CONCEPT_IDENTITY:
+ String conceptIdentity = ReaderUtils.getStringJsonField(parser);
+ if (conceptIdentity != null) {
+ timeDimension.setConceptIdentity(new IdentifiableArtefactReferenceImpl(conceptIdentity));
+ }
+ break;
+ case StructureUtils.LOCAL_REPRESENTATION:
+ timeDimension.setLocalRepresentation(getRepresentation(parser));
+ break;
+ default:
+ identifiableReader.read(timeDimension, parser);
+ break;
+ }
+ }
+ return timeDimension;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private DimensionImpl getDimension(JsonParser parser) {
+ try {
+ DimensionImpl dimension = new DimensionImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.POSITION:
+ parser.nextToken();
+ dimension.setOrder(parser.getIntValue());
+ break;
+ case StructureUtils.TYPE:
+ parser.nextToken();
+ break;
+ case StructureUtils.CONCEPT_IDENTITY:
+ String conceptIdentity = ReaderUtils.getStringJsonField(parser);
+ if (conceptIdentity != null) {
+ dimension.setConceptIdentity(new IdentifiableArtefactReferenceImpl(conceptIdentity));
+ }
+ break;
+ case StructureUtils.CONCEPT_ROLES:
+ List conceptRoles = ConceptRoleUtils.getConceptRoles(parser);
+ if (CollectionUtils.isNotEmpty(conceptRoles)) {
+ dimension.setConceptRoles(conceptRoles);
+ }
+ break;
+ case StructureUtils.LOCAL_REPRESENTATION:
+ dimension.setLocalRepresentation(getRepresentation(parser));
+ break;
+ default:
+ identifiableReader.read(dimension, parser);
+ break;
+ }
+ }
+ return dimension;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private Representation getRepresentation(JsonParser parser) throws IOException {
+ RepresentationReader representationReader = new RepresentationReader();
+ return representationReader.getRepresentations(parser);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeoFeatureSetCodeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeoFeatureSetCodeReader.java
new file mode 100644
index 0000000..ecea57e
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeoFeatureSetCodeReader.java
@@ -0,0 +1,18 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+
+import com.epam.jsdmx.infomodel.sdmx30.GeoFeatureSetCodeImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+public class GeoFeatureSetCodeReader implements CodeReader {
+ @Override
+ public void readExtendedCodeFields(JsonParser parser, GeoFeatureSetCodeImpl code) throws IOException {
+ String currentName = parser.getCurrentName();
+ if (StructureUtils.VALUE.equals(currentName)) {
+ code.setValue(ReaderUtils.getStringJsonField(parser));
+ }
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeoGridCodelistReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeoGridCodelistReader.java
new file mode 100644
index 0000000..456cc57
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeoGridCodelistReader.java
@@ -0,0 +1,95 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistExtension;
+import com.epam.jsdmx.infomodel.sdmx30.GeoGridCodelistImpl;
+import com.epam.jsdmx.infomodel.sdmx30.GridCodeImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class GeoGridCodelistReader extends MaintainableReader {
+
+ private final CodelistExtensionReader codelistExtensionReader;
+ private final GridCodeReader gridCodeReader;
+ private final NameableReader nameableReader;
+
+ Map codeWithParentId = new HashMap<>();
+
+ protected GeoGridCodelistReader(VersionableReader versionableArtefact,
+ CodelistExtensionReader codelistExtensionReader,
+ GridCodeReader gridCodeReader,
+ NameableReader nameableReader) {
+ super(versionableArtefact);
+ this.codelistExtensionReader = codelistExtensionReader;
+ this.gridCodeReader = gridCodeReader;
+ this.nameableReader = nameableReader;
+ }
+
+ @Override
+ protected GeoGridCodelistImpl createMaintainableArtefact() {
+ return new GeoGridCodelistImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, GeoGridCodelistImpl geoGridCodelist) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.GEO_GRID_CODES:
+ List geoFeatureSetCodes = ReaderUtils.getArray(parser, (this::getGeoGridCodes));
+ if (CollectionUtils.isNotEmpty(geoFeatureSetCodes)) {
+ geoGridCodelist.setItems(List.copyOf(geoFeatureSetCodes));
+ }
+ break;
+ case StructureUtils.GEO_TYPE:
+ parser.nextToken();
+ parser.nextToken();
+ break;
+ case StructureUtils.CODE_LIST_EXTENSIONS:
+ parser.nextToken();
+ List codeListExtensions = codelistExtensionReader.getCodeListExtensions(parser);
+ if (CollectionUtils.isNotEmpty(codeListExtensions)) {
+ geoGridCodelist.setExtensions(codeListExtensions);
+ }
+ break;
+ case StructureUtils.GRID_DEFINITION:
+ geoGridCodelist.setGridDefinition(ReaderUtils.getStringJsonField(parser));
+ break;
+ case StructureUtils.IS_PARTIAL:
+ geoGridCodelist.setPartial(ReaderUtils.getBooleanJsonField(parser));
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "GeographicalCodelist: " + fieldName);
+ }
+ }
+
+ private GridCodeImpl getGeoGridCodes(JsonParser parser) {
+ try {
+ return gridCodeReader.getCode(parser, new GridCodeImpl(), nameableReader, codeWithParentId);
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.GEO_GRID_CODELISTS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getGeoGridCodelists().addAll(artefacts);
+ }
+
+ @Override
+ protected void clean() {
+ codeWithParentId.clear();
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeographicCodelistReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeographicCodelistReader.java
new file mode 100644
index 0000000..caadeea
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GeographicCodelistReader.java
@@ -0,0 +1,93 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.CodelistExtension;
+import com.epam.jsdmx.infomodel.sdmx30.GeoFeatureSetCode;
+import com.epam.jsdmx.infomodel.sdmx30.GeoFeatureSetCodeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.GeographicCodelistImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import org.apache.commons.collections.CollectionUtils;
+
+public class GeographicCodelistReader extends MaintainableReader {
+
+ private final CodelistExtensionReader codelistExtensionReader;
+ private final GeoFeatureSetCodeReader geoFeatureSetCodeReader;
+ private final NameableReader nameableReader;
+
+ Map codeWithParentId = new HashMap<>();
+
+ protected GeographicCodelistReader(VersionableReader versionableArtefact,
+ NameableReader nameableReader,
+ CodelistExtensionReader codelistExtensionReader,
+ GeoFeatureSetCodeReader geoFeatureSetCodeReader) {
+ super(versionableArtefact);
+ this.codelistExtensionReader = codelistExtensionReader;
+ this.nameableReader = nameableReader;
+ this.geoFeatureSetCodeReader = geoFeatureSetCodeReader;
+ }
+
+ @Override
+ protected GeographicCodelistImpl createMaintainableArtefact() {
+ return new GeographicCodelistImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, GeographicCodelistImpl geographicCodelist) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.GEO_FEATURE_SET_CODES:
+ List geoFeatureSetCodes = ReaderUtils.getArray(parser, (this::getGeographicalCode));
+ if (CollectionUtils.isNotEmpty(geoFeatureSetCodes)) {
+ geographicCodelist.setItems(List.copyOf(geoFeatureSetCodes));
+ }
+ break;
+ case StructureUtils.GEO_TYPE:
+ parser.nextToken();
+ parser.nextToken();
+ break;
+ case StructureUtils.CODE_LIST_EXTENSIONS:
+ parser.nextToken();
+ List codeListExtensions = codelistExtensionReader.getCodeListExtensions(parser);
+ if (CollectionUtils.isNotEmpty(codeListExtensions)) {
+ geographicCodelist.setExtensions(codeListExtensions);
+ }
+ break;
+ case StructureUtils.IS_PARTIAL:
+ geographicCodelist.setPartial(ReaderUtils.getBooleanJsonField(parser));
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "GeographicalCodelist: " + fieldName);
+ }
+ }
+
+ private GeoFeatureSetCodeImpl getGeographicalCode(JsonParser parser) {
+ try {
+ return geoFeatureSetCodeReader.getCode(parser, new GeoFeatureSetCodeImpl(), nameableReader, codeWithParentId);
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.GEOGRAPHIC_CODELISTS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getGeographicCodelists().addAll(artefacts);
+ }
+
+ @Override
+ protected void clean() {
+ codeWithParentId.clear();
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GridCodeReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GridCodeReader.java
new file mode 100644
index 0000000..453e745
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/GridCodeReader.java
@@ -0,0 +1,19 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+
+import com.epam.jsdmx.infomodel.sdmx30.GridCodeImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+public class GridCodeReader implements CodeReader {
+
+ @Override
+ public void readExtendedCodeFields(JsonParser parser, GridCodeImpl code) throws IOException {
+ String currentName = parser.getCurrentName();
+ if (StructureUtils.GEO_CELL.equals(currentName)) {
+ code.setGeoCell(ReaderUtils.getStringJsonField(parser));
+ }
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/HierarchyAssociationReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/HierarchyAssociationReader.java
new file mode 100644
index 0000000..8af0815
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/HierarchyAssociationReader.java
@@ -0,0 +1,60 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchyAssociationImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactReference;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+public class HierarchyAssociationReader extends MaintainableReader {
+
+ public HierarchyAssociationReader(VersionableReader versionableReader) {
+ super(versionableReader);
+ }
+
+ @Override
+ protected HierarchyAssociationImpl createMaintainableArtefact() {
+ return new HierarchyAssociationImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, HierarchyAssociationImpl hierarchyAssociation) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.LINKED_HIERARCHY:
+ String linkedHierarchy = ReaderUtils.getStringJsonField(parser);
+ if (linkedHierarchy != null) {
+ hierarchyAssociation.setLinkedHierarchy(new MaintainableArtefactReference(linkedHierarchy));
+ }
+ break;
+ case StructureUtils.CONTEXT_OBJECT:
+ String contextObject = ReaderUtils.getStringJsonField(parser);
+ if (contextObject != null) {
+ hierarchyAssociation.setContextObject(new MaintainableArtefactReference(contextObject));
+ }
+ break;
+ case StructureUtils.LINKED_OBJECT:
+ String linkedObject = ReaderUtils.getStringJsonField(parser);
+ if (linkedObject != null) {
+ hierarchyAssociation.setLinkedObject(new MaintainableArtefactReference(linkedObject));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "HierarchyAssociation: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.HIERARCHY_ASSOCIATIONS;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getHierarchyAssociations().addAll(artefacts);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/HierarchyReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/HierarchyReader.java
new file mode 100644
index 0000000..c6e6140
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/HierarchyReader.java
@@ -0,0 +1,229 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.BaseFacetImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CodingFormat;
+import com.epam.jsdmx.infomodel.sdmx30.CodingFormatImpl;
+import com.epam.jsdmx.infomodel.sdmx30.FacetType;
+import com.epam.jsdmx.infomodel.sdmx30.FacetValueType;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchicalCode;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchicalCodeImpl;
+import com.epam.jsdmx.infomodel.sdmx30.HierarchyImpl;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.LevelImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class HierarchyReader extends MaintainableReader {
+
+ public final IdentifiableReader identifiableReader;
+
+ private final NameableReader nameableReader;
+
+ public HierarchyReader(VersionableReader versionableArtefact,
+ IdentifiableReader identifiableReader,
+ NameableReader nameableReader) {
+ super(versionableArtefact);
+ this.identifiableReader = identifiableReader;
+ this.nameableReader = nameableReader;
+ }
+
+ @Override
+ protected HierarchyImpl createMaintainableArtefact() {
+ return new HierarchyImpl();
+ }
+
+ @Override
+ public void readArtefact(JsonParser parser, HierarchyImpl hierarchy) throws IOException {
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.HIERARCHICAL_CODES:
+ List hierarchicalCodes = ReaderUtils.getArray(parser, this::getHierarchicalCode);
+ if (CollectionUtils.isNotEmpty(hierarchicalCodes)) {
+ hierarchy.setCodes(hierarchicalCodes);
+ }
+ break;
+ case StructureUtils.HAS_FORMAT_LEVELS:
+ boolean hasFormatLevels = ReaderUtils.getBooleanJsonField(parser);
+ hierarchy.setHasFormalLevels(hasFormatLevels);
+ break;
+ case StructureUtils.LEVEL:
+ hierarchy.setLevel(getLevel(parser));
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "Hierarchy: " + fieldName);
+ }
+ }
+
+ @Override
+ protected String getName() {
+ return StructureUtils.HIERARCHIES;
+ }
+
+ @Override
+ protected void setArtefacts(Artefacts artefact, Set artefacts) {
+ artefact.getHierarchies().addAll(artefacts);
+ }
+
+ private LevelImpl getLevel(JsonParser parser) throws IOException {
+ parser.nextToken();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return null;
+ }
+ LevelImpl level = new LevelImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.LEVEL:
+ level.setChild(getLevel(parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.CODING_FORMAT:
+ level.setCodeFormat(getCodingFormats(parser));
+ parser.nextToken();
+ break;
+ default:
+ nameableReader.read(level, parser);
+ parser.nextToken();
+ break;
+ }
+ }
+ return level;
+ }
+
+ private List getCodingFormats(JsonParser parser) throws IOException {
+ List codingFormats = new ArrayList<>();
+ parser.nextToken();
+ if (parser.currentToken()
+ .equals(JsonToken.START_OBJECT) && parser.nextToken()
+ .equals(JsonToken.END_OBJECT)) {
+ return List.of();
+ }
+ while (!JsonToken.END_OBJECT.equals(parser.currentToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.DATA_TYPE:
+ String dataType = ReaderUtils.getStringJsonField(parser);
+ if (dataType != null) {
+ CodingFormatImpl codingFormat = new CodingFormatImpl();
+ codingFormat.setCodingFormat(new BaseFacetImpl(FacetValueType.fromValue(dataType)));
+ codingFormats.add(codingFormat);
+ }
+ parser.nextToken();
+ break;
+ case StructureUtils.IS_SEQUENCE:
+ codingFormats.add(setFacet(FacetType.IS_SEQUENCE, parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.INTERVAL:
+ codingFormats.add(setFacet(FacetType.INTERVAL, parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.START_VALUE:
+ codingFormats.add(setFacet(FacetType.START_VALUE, parser));
+ break;
+ case StructureUtils.END_VALUE:
+ codingFormats.add(setFacet(FacetType.END_VALUE, parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.MIN_LENGTH:
+ codingFormats.add(setFacet(FacetType.MIN_LENGTH, parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.MAX_LENGTH:
+ codingFormats.add(setFacet(FacetType.MAX_LENGTH, parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.MIN_VALUE:
+ codingFormats.add(setFacet(FacetType.MIN_VALUE, parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.MAX_VALUE:
+ codingFormats.add(setFacet(FacetType.MAX_VALUE, parser));
+ parser.nextToken();
+ break;
+ case StructureUtils.PATTERN:
+ codingFormats.add(setFacet(FacetType.PATTERN, parser));
+ parser.nextToken();
+ break;
+ default:
+ throw new IllegalArgumentException(StructureUtils.NO_SUCH_PROPERTY_IN + "CodingFormat:" + fieldName);
+ }
+ }
+ return codingFormats;
+ }
+
+ private CodingFormatImpl setFacet(FacetType startValue, JsonParser parser) throws IOException {
+ BaseFacetImpl baseFacetStartValue = new BaseFacetImpl();
+ baseFacetStartValue.setType(startValue);
+ baseFacetStartValue.setValue(ReaderUtils.getFieldAsString(parser));
+ CodingFormatImpl codingFormat = new CodingFormatImpl();
+ codingFormat.setCodingFormat(baseFacetStartValue);
+ return codingFormat;
+ }
+
+ private HierarchicalCode getHierarchicalCode(JsonParser parser) {
+ try {
+ HierarchicalCodeImpl hierarchicalCode = new HierarchicalCodeImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.HIERARCHICAL_CODES:
+ parser.nextToken();
+ setHierarchicalCodes(parser, hierarchicalCode);
+ break;
+ case StructureUtils.CODE:
+ String code = ReaderUtils.getStringJsonField(parser);
+ if (code != null) {
+ hierarchicalCode.setCode(new IdentifiableArtefactReferenceImpl(code));
+ }
+ break;
+ case StructureUtils.VALID_TO:
+ Instant validTo = ReaderUtils.getInstantObj(parser);
+ if (validTo != null) {
+ hierarchicalCode.setValidTo(validTo);
+ }
+ break;
+ case StructureUtils.VALID_FROM:
+ Instant validFrom = ReaderUtils.getInstantObj(parser);
+ if (validFrom != null) {
+ hierarchicalCode.setValidFrom(validFrom);
+ }
+ break;
+ case StructureUtils.LEVEL:
+ hierarchicalCode.setLevelId(ReaderUtils.getStringJsonField(parser));
+ break;
+ default:
+ identifiableReader.read(hierarchicalCode, parser);
+ break;
+ }
+ }
+ return hierarchicalCode;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private void setHierarchicalCodes(JsonParser parser, HierarchicalCodeImpl hierarchicalCode) throws IOException {
+ List hierarchicalCodes = ReaderUtils.getArray(parser, this::getHierarchicalCode);
+ if (CollectionUtils.isNotEmpty(hierarchicalCodes)) {
+ hierarchicalCode.setHierarchicalCodes(hierarchicalCodes);
+ }
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/IdentifiableReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/IdentifiableReader.java
new file mode 100644
index 0000000..4b74d15
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/IdentifiableReader.java
@@ -0,0 +1,29 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefact;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+public class IdentifiableReader {
+
+ private final AnnotableReader annotableReader;
+
+ public IdentifiableReader(AnnotableReader annotableReader) {
+ this.annotableReader = annotableReader;
+ }
+
+ public IdentifiableArtefact read(IdentifiableArtefactImpl identifiableArtefact, JsonParser parser) throws IOException {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ if (StructureUtils.ID.equals(fieldName)) {
+ identifiableArtefact.setId(ReaderUtils.getStringJsonField(parser));
+ } else {
+ annotableReader.read(identifiableArtefact, parser);
+ }
+ return identifiableArtefact;
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ItemMapReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ItemMapReader.java
new file mode 100644
index 0000000..08079c9
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/ItemMapReader.java
@@ -0,0 +1,59 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getFieldAsString;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getInstantObj;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import com.epam.jsdmx.infomodel.sdmx30.ItemMap;
+import com.epam.jsdmx.infomodel.sdmx30.ItemMapImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactReference;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.lang3.StringUtils;
+
+public interface ItemMapReader {
+
+ default ItemMap getItemMap(JsonParser parser) {
+ ItemMapImpl itemMap = new ItemMapImpl();
+ try {
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.SOURCE_VALUE:
+ itemMap.setSource(getFieldAsString(parser));
+ break;
+ case StructureUtils.TARGET_VALUE:
+ itemMap.setTarget(getFieldAsString(parser));
+ break;
+ case StructureUtils.VALID_FROM:
+ itemMap.setValidFrom(getInstantObj(parser));
+ break;
+ case StructureUtils.VALID_TO:
+ itemMap.setValidTo(getInstantObj(parser));
+ break;
+ default:
+ AnnotableReader annotableReader = new AnnotableReader();
+ annotableReader.read(itemMap, parser);
+ break;
+ }
+ }
+ return itemMap;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ default Optional getTargetSource(JsonParser parser) throws IOException {
+ String target = ReaderUtils.getStringJsonField(parser);
+ if (StringUtils.isBlank(target)) {
+ return Optional.empty();
+ }
+ return Optional.of(new MaintainableArtefactReference(target));
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonReaderFactory.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonReaderFactory.java
new file mode 100644
index 0000000..f0a03d7
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonReaderFactory.java
@@ -0,0 +1,254 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactImpl;
+
+public final class JsonReaderFactory {
+
+ private JsonReaderFactory() {
+ }
+
+ public static DataReader newInstance() {
+ return new DataReader(readers());
+ }
+
+ private static AnnotableReader getAnnotableReader() {
+ return new AnnotableReader();
+ }
+
+ private static IdentifiableReader getIdentifiableReader() {
+ return new IdentifiableReader(getAnnotableReader());
+ }
+
+ private static NameableReader getNameableReader() {
+ return new NameableReader(getIdentifiableReader());
+ }
+
+ private static VersionableReader getVersionableReader() {
+ return new VersionableReader(getNameableReader());
+ }
+
+ private static ContactReader getContactReader() {
+ return new ContactReader();
+ }
+
+ private static OrganisationReader getOrganisationReader() {
+ return new OrganisationReader(getContactReader(), getNameableReader());
+ }
+
+ private static MemberSelectionReader getMemberSelectionReader() {
+ return new MemberSelectionReader();
+ }
+
+ private static AgencySchemeReader getAgencySchemeReader() {
+ return new AgencySchemeReader(getVersionableReader(), getOrganisationReader());
+ }
+
+ private static CategorisationReader getCategorisationReader() {
+ return new CategorisationReader(getVersionableReader());
+ }
+
+ private static CategorySchemeMapReader getCategorySchemeMapReader() {
+ return new CategorySchemeMapReader(getVersionableReader());
+ }
+
+ private static CategorySchemeReader getCategorySchemeReader() {
+ return new CategorySchemeReader(getVersionableReader(), getNameableReader());
+ }
+
+ private static CodelistReader getCodelistReader() {
+ return new CodelistReader(getVersionableReader(), getNameableReader(), getCodelistExtensionReader(), getCodeImplReader());
+ }
+
+ private static CodeImplReader getCodeImplReader() {
+ return new CodeImplReader();
+ }
+
+ private static CodelistExtensionReader getCodelistExtensionReader() {
+ return new CodelistExtensionReader();
+ }
+
+ private static GeoFeatureSetCodeReader getGeoFeatureSetCodeReader() {
+ return new GeoFeatureSetCodeReader();
+ }
+
+ private static GridCodeReader getGridCodeReader() {
+ return new GridCodeReader();
+ }
+
+ private static ConceptSchemeMapReader getConceptSchemeMapReader() {
+ return new ConceptSchemeMapReader(getVersionableReader());
+ }
+
+ private static ConceptSchemeReader getConceptSchemeReader() {
+ return new ConceptSchemeReader(getVersionableReader(), getNameableReader());
+ }
+
+ private static DataConsumerSchemeReader getDataConsumerSchemeReader() {
+ return new DataConsumerSchemeReader(getVersionableReader(), getOrganisationReader());
+ }
+
+ private static DataFlowReader getDataflowReader() {
+ return new DataFlowReader(getVersionableReader());
+ }
+
+ private static DataProviderSchemeReader getDataProviderSchemeReader() {
+ return new DataProviderSchemeReader(getVersionableReader(), getOrganisationReader());
+ }
+
+ private static DataStructureDefinitionReader getDataStructureDefinitionReader() {
+ return new DataStructureDefinitionReader(
+ getVersionableReader(),
+ getIdentifiableReader()
+ );
+ }
+
+ private static HierarchyAssociationReader getHierarchyAssociationReader() {
+ return new HierarchyAssociationReader(getVersionableReader());
+ }
+
+ private static HierarchyReader getHierarchyReader() {
+ return new HierarchyReader(getVersionableReader(), getIdentifiableReader(), getNameableReader());
+ }
+
+ private static MetadataConstraintReader getMetadataConstraintReader() {
+ return new MetadataConstraintReader(getVersionableReader(), getMemberSelectionReader(), getReleaseCalendarReader());
+ }
+
+ private static ReleaseCalendarReader getReleaseCalendarReader() {
+ return new ReleaseCalendarReader();
+ }
+
+ private static MetadataFlowReader getMetadataflowReader() {
+ return new MetadataFlowReader(getVersionableReader());
+ }
+
+ private static MetadataProviderSchemeReader getMetadataProviderSchemeReader() {
+ return new MetadataProviderSchemeReader(getVersionableReader(), getOrganisationReader());
+ }
+
+ private static MetadataProvisionAgreementReader getMetadataProvisionAgreementReader() {
+ return new MetadataProvisionAgreementReader(getVersionableReader());
+ }
+
+ private static MetaDataStructureDefinitionReader getMetadataStructureDefinitionReader() {
+ return new MetaDataStructureDefinitionReader(
+ getVersionableReader(),
+ getIdentifiableReader()
+ );
+ }
+
+ private static OrganisationSchemeMapReader getOrganisationSchemeMapReader() {
+ return new OrganisationSchemeMapReader(getVersionableReader());
+ }
+
+ private static OrganisationUnitSchemeReader getOrganisationUnitSchemeReader() {
+ return new OrganisationUnitSchemeReader(getVersionableReader(), getOrganisationReader());
+ }
+
+ private static ProcessReader getProcessReader() {
+ return new ProcessReader(
+ getVersionableReader(),
+ getNameableReader(),
+ getAnnotableReader(),
+ getIdentifiableReader()
+ );
+ }
+
+ private static ProvisionAgreementReader getProvisionAgreementReader() {
+ return new ProvisionAgreementReader(getVersionableReader());
+ }
+
+ private static ReportingTaxonomyReader getReportingTaxonomyReader() {
+ return new ReportingTaxonomyReader(getVersionableReader(), getNameableReader());
+ }
+
+ private static ReportingTaxonomyMapReader getReportingTaxonomyMapReader() {
+ return new ReportingTaxonomyMapReader(getVersionableReader());
+ }
+
+ private static RepresentationMapReader getRepresentationMapReader() {
+ return new RepresentationMapReader(getVersionableReader());
+ }
+
+ private static StructureMapReader getStructureMapReader() {
+ return new StructureMapReader(
+ getVersionableReader(),
+ getIdentifiableReader(),
+ getAnnotableReader()
+ );
+ }
+
+ private static ValueListReader getValueListReader() {
+ return new ValueListReader(
+ getVersionableReader(),
+ getAnnotableReader()
+ );
+ }
+
+ private static DataKeySetReader getDataKeySetReader() {
+ return new DataKeySetReader(getAnnotableReader(), getMemberSelectionReader());
+ }
+
+ private static CubeRegionReader getCubeRegionReader() {
+ return new CubeRegionReader(getMemberSelectionReader(), getAnnotableReader());
+ }
+
+ private static DataConstraintReader getDataConstraintsReader() {
+ return new DataConstraintReader(getVersionableReader(), getReleaseCalendarReader(), getDataKeySetReader(), getCubeRegionReader());
+ }
+
+ private static GeographicCodelistReader getGeographicCodelistReader() {
+ return new GeographicCodelistReader(
+ getVersionableReader(),
+ getNameableReader(),
+ getCodelistExtensionReader(),
+ getGeoFeatureSetCodeReader()
+ );
+ }
+
+ private static GeoGridCodelistReader getGeoGridCodelistReader() {
+ return new GeoGridCodelistReader(
+ getVersionableReader(),
+ getCodelistExtensionReader(),
+ getGridCodeReader(),
+ getNameableReader()
+ );
+ }
+
+ private static List extends MaintainableReader extends MaintainableArtefactImpl>> readers() {
+ return List.of(
+ getAgencySchemeReader(),
+ getCategorisationReader(),
+ getCategorySchemeReader(),
+ getCategorySchemeMapReader(),
+ getCodelistReader(),
+ getConceptSchemeReader(),
+ getConceptSchemeMapReader(),
+ getDataConstraintsReader(),
+ getDataConsumerSchemeReader(),
+ getDataProviderSchemeReader(),
+ getDataStructureDefinitionReader(),
+ getDataflowReader(),
+ getHierarchyReader(),
+ getHierarchyAssociationReader(),
+ getMetadataConstraintReader(),
+ getMetadataProviderSchemeReader(),
+ getMetadataProvisionAgreementReader(),
+ getMetadataStructureDefinitionReader(),
+ getMetadataflowReader(),
+ getOrganisationSchemeMapReader(),
+ getOrganisationUnitSchemeReader(),
+ getProcessReader(),
+ getProvisionAgreementReader(),
+ getReportingTaxonomyReader(),
+ getReportingTaxonomyMapReader(),
+ getRepresentationMapReader(),
+ getStructureMapReader(),
+ getValueListReader(),
+ getGeographicCodelistReader(),
+ getGeoGridCodelistReader()
+ );
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonRuntimeException.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonRuntimeException.java
new file mode 100644
index 0000000..07d2b42
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonRuntimeException.java
@@ -0,0 +1,11 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+public class JsonRuntimeException extends RuntimeException {
+ public JsonRuntimeException(String message) {
+ super(message);
+ }
+
+ public JsonRuntimeException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonStructureReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonStructureReader.java
new file mode 100644
index 0000000..999f7d2
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/JsonStructureReader.java
@@ -0,0 +1,78 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactsImpl;
+import com.epam.jsdmx.serializer.sdmx30.common.DataLocation;
+import com.epam.jsdmx.serializer.sdmx30.common.Header;
+import com.epam.jsdmx.serializer.sdmx30.structure.StructureReader;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Streaming deserialization for json (2.0)
+ */
+@Slf4j
+public class JsonStructureReader implements StructureReader {
+
+ private final DataReader dataReader;
+ private final MetaReader metaReader;
+
+ public JsonStructureReader(DataReader dataReader, MetaReader metaReader) {
+ this.dataReader = dataReader;
+ this.metaReader = metaReader;
+ }
+
+ @Override
+ public ArtefactsImpl read(DataLocation location) {
+ final InputStream inputStream = location.inputStream();
+ JsonFactory factory = new JsonFactory();
+ ArtefactsImpl artefacts = new ArtefactsImpl();
+ try (JsonParser parser = factory.createParser(inputStream)) {
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case "data":
+ parser.nextToken();
+ artefacts = getData(parser);
+ break;
+ case "meta":
+ parser.nextToken();
+ getHeader(parser);
+ break;
+ case "error":
+ parser.nextToken();
+ break;
+ default:
+ throw new IllegalArgumentException("Not correct structure: " + fieldName);
+ }
+ }
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ return artefacts;
+ }
+
+ private Header getHeader(JsonParser parser) throws IOException {
+ return metaReader.read(parser);
+ }
+
+ private ArtefactsImpl getData(JsonParser parser) throws IOException {
+ return dataReader.read(parser);
+ }
+
+ public ArtefactsImpl readAndClose(DataLocation location) {
+ final InputStream inputStream = location.inputStream();
+ try (inputStream) {
+ return read(location);
+ } catch (IOException e) {
+ log.error(e.getMessage());
+ }
+ return null;
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MaintainableReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MaintainableReader.java
new file mode 100644
index 0000000..c58dbc9
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MaintainableReader.java
@@ -0,0 +1,82 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getStringJsonField;
+
+import java.io.IOException;
+import java.util.Set;
+
+import com.epam.jsdmx.infomodel.sdmx30.Artefacts;
+import com.epam.jsdmx.infomodel.sdmx30.MaintainableArtefactImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public abstract class MaintainableReader {
+
+ private final VersionableReader versionableArtefact;
+
+ protected MaintainableReader(VersionableReader versionableArtefact) {
+ this.versionableArtefact = versionableArtefact;
+ }
+
+ protected abstract T createMaintainableArtefact();
+
+ public abstract void readArtefact(JsonParser parser, T maintainableArtefact) throws IOException;
+
+ public T read(JsonParser parser) throws IOException {
+ T maintainable = createMaintainableArtefact();
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ if (!checkIsNotEmptyObjectAndSkipUntilFieldName(parser)) {
+ return null;
+ }
+ String fieldname = parser.getCurrentName();
+ switch (fieldname) {
+ case StructureUtils.ID:
+ case StructureUtils.ANNOTATIONS:
+ case StructureUtils.VERSION:
+ case StructureUtils.VALID_TO:
+ case StructureUtils.VALID_FROM:
+ case StructureUtils.NAMES:
+ case StructureUtils.DESCRIPTIONS:
+ case StructureUtils.DESCRIPTION:
+ case StructureUtils.NAME:
+ versionableArtefact.read(maintainable, parser);
+ break;
+ case StructureUtils.AGENCY_ID:
+ maintainable.setOrganizationId(getStringJsonField(parser));
+ break;
+ case StructureUtils.LINKS:
+ parser.nextToken();
+ parser.skipChildren();
+ break;
+ case StructureUtils.IS_EXTERNAL_REFERENCE:
+ parser.nextToken();
+ break;
+ default:
+ readArtefact(parser, maintainable);
+ }
+ }
+ return maintainable;
+ }
+
+ public MaintainableArtefactImpl readAndClose(JsonParser parser) throws IOException {
+ try (parser) {
+ return read(parser);
+ } catch (IOException e) {
+ log.error(e.getMessage());
+ throw e;
+ }
+ }
+
+ protected abstract String getName();
+
+ protected abstract void setArtefacts(Artefacts artefact, Set artefacts);
+
+ protected void clean() {
+ // individual for each artefact
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MeasureListReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MeasureListReader.java
new file mode 100644
index 0000000..c077416
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MeasureListReader.java
@@ -0,0 +1,88 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.ArtefactReference;
+import com.epam.jsdmx.infomodel.sdmx30.IdentifiableArtefactReferenceImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Measure;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureDescriptorImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MeasureImpl;
+import com.epam.jsdmx.infomodel.sdmx30.Representation;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class MeasureListReader {
+
+ private final IdentifiableReader identifiableReader = new IdentifiableReader(
+ new AnnotableReader());
+
+ public MeasureDescriptorImpl getMeasureDescriptor(JsonParser parser) throws IOException {
+ parser.nextToken();
+ if (ReaderUtils.isNullValue(parser)) {
+ return null;
+ }
+ MeasureDescriptorImpl measureDescriptor = new MeasureDescriptorImpl();
+ List measures = new ArrayList<>();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ if (StructureUtils.MEASURES.equals(fieldName)) {
+ List measureList = ReaderUtils.getArray(parser, this::getMeasure);
+ if (CollectionUtils.isNotEmpty(measureList)) {
+ measures.addAll(measureList);
+ }
+ } else {
+ identifiableReader.read(measureDescriptor, parser);
+ }
+ }
+ measureDescriptor.setComponents(measures);
+ measureDescriptor.setId(StructureUtils.MEASURE_DESCRIPTOR_ID);
+ return measureDescriptor;
+ }
+
+ private Measure getMeasure(JsonParser parser) {
+ try {
+ MeasureImpl measure = new MeasureImpl();
+ while (!JsonToken.END_OBJECT.equals(parser.nextToken())) {
+ ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.IS_MANDATORY:
+ parser.nextToken();
+ break;
+ case StructureUtils.CONCEPT_IDENTITY:
+ String conceptIdentity = ReaderUtils.getStringJsonField(parser);
+ if (conceptIdentity != null) {
+ measure.setConceptIdentity(new IdentifiableArtefactReferenceImpl(conceptIdentity));
+ }
+ break;
+ case StructureUtils.CONCEPT_ROLES:
+ List conceptRoles = ConceptRoleUtils.getConceptRoles(parser);
+ if (CollectionUtils.isNotEmpty(conceptRoles)) {
+ measure.setConceptRoles(conceptRoles);
+ }
+ break;
+ case StructureUtils.LOCAL_REPRESENTATION:
+ measure.setLocalRepresentation(getRepresentation(parser));
+ break;
+ default:
+ identifiableReader.read(measure, parser);
+ break;
+ }
+ }
+ return measure;
+ } catch (IOException e) {
+ throw new JsonRuntimeException(e);
+ }
+ }
+
+ private Representation getRepresentation(JsonParser parser) throws IOException {
+ RepresentationReader representationReader = new RepresentationReader();
+ return representationReader.getRepresentations(parser);
+ }
+}
diff --git a/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MemberSelectionReader.java b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MemberSelectionReader.java
new file mode 100644
index 0000000..db57dfd
--- /dev/null
+++ b/sdmx-json20/src/main/java/com/epam/jsdmx/json20/structure/reader/MemberSelectionReader.java
@@ -0,0 +1,196 @@
+package com.epam.jsdmx.json20.structure.reader;
+
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.checkIsNotEmptyObjectAndSkipUntilFieldName;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getBooleanJsonField;
+import static com.epam.jsdmx.json20.structure.reader.ReaderUtils.getFieldAsString;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.epam.jsdmx.infomodel.sdmx30.AfterPeriodImpl;
+import com.epam.jsdmx.infomodel.sdmx30.BeforePeriodImpl;
+import com.epam.jsdmx.infomodel.sdmx30.CascadeValue;
+import com.epam.jsdmx.infomodel.sdmx30.LocalisedMemberValueImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelection;
+import com.epam.jsdmx.infomodel.sdmx30.MemberSelectionImpl;
+import com.epam.jsdmx.infomodel.sdmx30.MemberValueImpl;
+import com.epam.jsdmx.infomodel.sdmx30.RangePeriodImpl;
+import com.epam.jsdmx.infomodel.sdmx30.SelectionValue;
+import com.epam.jsdmx.infomodel.sdmx30.TimeRangePeriodImpl;
+import com.epam.jsdmx.json20.structure.writer.StructureUtils;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.commons.collections.CollectionUtils;
+
+public class MemberSelectionReader {
+
+ public MemberSelection getMemberSelection(JsonParser parser) {
+ try {
+ MemberSelectionImpl memberSelection = new MemberSelectionImpl();
+ List selectionValues = new ArrayList<>();
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ checkIsNotEmptyObjectAndSkipUntilFieldName(parser);
+ String fieldName = parser.getCurrentName();
+ switch (fieldName) {
+ case StructureUtils.INCLUDE:
+ memberSelection.setIncluded(getBooleanJsonField(parser));
+ break;
+ case StructureUtils.ID:
+ memberSelection.setComponentId(getFieldAsString(parser));
+ break;
+ case StructureUtils.REMOVE_PREFIX:
+ memberSelection.setRemovePrefix(getBooleanJsonField(parser));
+ break;
+ case StructureUtils.TIME_RANGE:
+ List timeRanges = getTimeRanges(parser);
+ if (CollectionUtils.isNotEmpty(timeRanges)) {
+ selectionValues.addAll(timeRanges);
+ }
+ break;
+ case StructureUtils.VALUES:
+ List