pysdmx 1.10.1__py3-none-any.whl → 1.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. pysdmx/__init__.py +1 -1
  2. pysdmx/api/fmr/maintenance.py +10 -5
  3. pysdmx/io/input_processor.py +4 -0
  4. pysdmx/io/json/fusion/messages/dsd.py +19 -14
  5. pysdmx/io/json/fusion/messages/msd.py +6 -9
  6. pysdmx/io/json/sdmxjson2/messages/core.py +12 -5
  7. pysdmx/io/json/sdmxjson2/messages/dsd.py +11 -17
  8. pysdmx/io/json/sdmxjson2/messages/msd.py +2 -5
  9. pysdmx/io/json/sdmxjson2/messages/report.py +7 -3
  10. pysdmx/io/json/sdmxjson2/messages/structure.py +7 -3
  11. pysdmx/io/json/sdmxjson2/reader/metadata.py +3 -3
  12. pysdmx/io/json/sdmxjson2/reader/structure.py +3 -3
  13. pysdmx/io/json/sdmxjson2/writer/_helper.py +118 -0
  14. pysdmx/io/json/sdmxjson2/writer/v2_0/__init__.py +1 -0
  15. pysdmx/io/json/sdmxjson2/writer/v2_0/metadata.py +33 -0
  16. pysdmx/io/json/sdmxjson2/writer/v2_0/structure.py +33 -0
  17. pysdmx/io/json/sdmxjson2/writer/v2_1/__init__.py +1 -0
  18. pysdmx/io/json/sdmxjson2/writer/v2_1/metadata.py +31 -0
  19. pysdmx/io/json/sdmxjson2/writer/v2_1/structure.py +33 -0
  20. pysdmx/io/reader.py +12 -3
  21. pysdmx/io/writer.py +13 -3
  22. pysdmx/io/xml/__ss_aux_reader.py +39 -17
  23. pysdmx/io/xml/__structure_aux_reader.py +221 -33
  24. pysdmx/io/xml/__structure_aux_writer.py +304 -5
  25. pysdmx/io/xml/__tokens.py +12 -0
  26. pysdmx/io/xml/__write_aux.py +9 -0
  27. pysdmx/io/xml/sdmx21/writer/generic.py +2 -2
  28. pysdmx/model/dataflow.py +2 -2
  29. pysdmx/toolkit/pd/_data_utils.py +1 -1
  30. {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/METADATA +7 -1
  31. {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/RECORD +33 -28
  32. {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/WHEEL +1 -1
  33. pysdmx/io/json/sdmxjson2/writer/metadata.py +0 -60
  34. pysdmx/io/json/sdmxjson2/writer/structure.py +0 -61
  35. {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,33 @@
1
+ """Writer interface for SDMX-JSON 2.1.0 Structure messages."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional, Sequence, Union
5
+
6
+ from pysdmx.io.json.sdmxjson2.writer._helper import write_structure_msg
7
+ from pysdmx.model.__base import MaintainableArtefact
8
+ from pysdmx.model.message import Header
9
+
10
+
11
+ def write(
12
+ structures: Sequence[MaintainableArtefact],
13
+ output_path: Optional[Union[str, Path]] = None,
14
+ prettyprint: bool = True,
15
+ header: Optional[Header] = None,
16
+ ) -> Optional[str]:
17
+ """Write maintainable SDMX artefacts in SDMX-JSON 2.1.0.
18
+
19
+ Args:
20
+ structures: The maintainable SDMX artefacts to be serialized.
21
+ output_path: The path to save the JSON file. If None or empty, the
22
+ serialized content is returned as a string instead.
23
+ prettyprint: Whether to format the JSON output with indentation (True)
24
+ or output compact JSON without extra whitespace (False).
25
+ header: The header to be used in the SDMX-JSON message
26
+ (will be generated if no header is supplied).
27
+
28
+ Returns:
29
+ The JSON string if output_path is None or empty, None otherwise.
30
+ """
31
+ return write_structure_msg(
32
+ structures, output_path, prettyprint, header, "2.1"
33
+ )
pysdmx/io/reader.py CHANGED
@@ -80,7 +80,10 @@ def read_sdmx( # noqa: C901
80
80
  header = read_header(input_str, validate=validate)
81
81
  # SDMX-ML 3.1 Structure
82
82
  result_structures = read_structure(input_str, validate=validate)
83
- elif read_format == Format.STRUCTURE_SDMX_JSON_2_0_0:
83
+ elif read_format in (
84
+ Format.STRUCTURE_SDMX_JSON_2_0_0,
85
+ Format.STRUCTURE_SDMX_JSON_2_1_0,
86
+ ):
84
87
  from pysdmx.io.json.sdmxjson2.reader.structure import (
85
88
  read as read_struct,
86
89
  )
@@ -90,7 +93,10 @@ def read_sdmx( # noqa: C901
90
93
  result_structures = (
91
94
  struct_msg.structures if struct_msg.structures else []
92
95
  )
93
- elif read_format == Format.REFMETA_SDMX_JSON_2_0_0:
96
+ elif read_format in (
97
+ Format.REFMETA_SDMX_JSON_2_0_0,
98
+ Format.REFMETA_SDMX_JSON_2_1_0,
99
+ ):
94
100
  from pysdmx.io.json.sdmxjson2.reader.metadata import (
95
101
  read as read_refmeta,
96
102
  )
@@ -174,7 +180,10 @@ def read_sdmx( # noqa: C901
174
180
  return Message(header=header, data=result_data)
175
181
  elif read_format == Format.REGISTRY_SDMX_ML_2_1:
176
182
  return Message(header=header, submission=result_submission)
177
- elif read_format == Format.REFMETA_SDMX_JSON_2_0_0:
183
+ elif read_format in (
184
+ Format.REFMETA_SDMX_JSON_2_0_0,
185
+ Format.REFMETA_SDMX_JSON_2_1_0,
186
+ ):
178
187
  return Message(header=header, reports=reports)
179
188
  # TODO: Ensure we have changed the signature of the structure readers
180
189
  return Message(header=header, structures=result_structures)
pysdmx/io/writer.py CHANGED
@@ -24,10 +24,16 @@ WRITERS = {
24
24
  Format.DATA_SDMX_ML_3_1: "pysdmx.io.xml.sdmx31.writer.structure_specific",
25
25
  Format.STRUCTURE_SDMX_ML_3_1: "pysdmx.io.xml.sdmx31.writer.structure",
26
26
  Format.STRUCTURE_SDMX_JSON_2_0_0: (
27
- "pysdmx.io.json.sdmxjson2.writer.structure"
27
+ "pysdmx.io.json.sdmxjson2.writer.v2_0.structure"
28
+ ),
29
+ Format.STRUCTURE_SDMX_JSON_2_1_0: (
30
+ "pysdmx.io.json.sdmxjson2.writer.v2_1.structure"
28
31
  ),
29
32
  Format.REFMETA_SDMX_JSON_2_0_0: (
30
- "pysdmx.io.json.sdmxjson2.writer.metadata"
33
+ "pysdmx.io.json.sdmxjson2.writer.v2_0.metadata"
34
+ ),
35
+ Format.REFMETA_SDMX_JSON_2_1_0: (
36
+ "pysdmx.io.json.sdmxjson2.writer.v2_1.metadata"
31
37
  ),
32
38
  }
33
39
 
@@ -36,9 +42,13 @@ STRUCTURE_WRITERS = (
36
42
  Format.STRUCTURE_SDMX_ML_3_0,
37
43
  Format.STRUCTURE_SDMX_ML_3_1,
38
44
  Format.STRUCTURE_SDMX_JSON_2_0_0,
45
+ Format.STRUCTURE_SDMX_JSON_2_1_0,
39
46
  )
40
47
 
41
- REFMETA_WRITERS = (Format.REFMETA_SDMX_JSON_2_0_0,)
48
+ REFMETA_WRITERS = (
49
+ Format.REFMETA_SDMX_JSON_2_0_0,
50
+ Format.REFMETA_SDMX_JSON_2_1_0,
51
+ )
42
52
 
43
53
 
44
54
  def write_sdmx(
@@ -1,7 +1,7 @@
1
1
  """SDMX XML StructureSpecificData reader aux module."""
2
2
 
3
3
  import itertools
4
- from typing import Any, Dict
4
+ from typing import Any, Dict, List
5
5
 
6
6
  import numpy as np
7
7
  import pandas as pd
@@ -40,23 +40,22 @@ def _reading_str_series(dataset: Dict[str, Any]) -> pd.DataFrame:
40
40
  return df
41
41
 
42
42
 
43
- def _reading_group_data(dataset: Dict[str, Any]) -> pd.DataFrame:
43
+ def _reading_group_data(dataset: Dict[str, Any]) -> List[pd.DataFrame]:
44
44
  # Structure Specific Group Data
45
- test_list = []
46
- df = None
45
+ group_dfs = []
47
46
  dataset[GROUP] = add_list(dataset[GROUP])
48
47
  for data in dataset[GROUP]:
49
- test_list.append(dict(data.items()))
50
- test_list, df = __process_df(test_list, df)
51
- test_list, df = __process_df(test_list, df, is_end=True)
48
+ group_dict = dict(data.items())
49
+ group_df = pd.DataFrame([group_dict])
52
50
 
53
- cols_to_delete = [x for x in df.columns if ":type" in x]
54
- for x in cols_to_delete:
55
- del df[x]
51
+ # Remove :type columns
52
+ cols_to_delete = [x for x in group_df.columns if ":type" in x]
53
+ for x in cols_to_delete:
54
+ del group_df[x]
56
55
 
57
- df = df.drop_duplicates(keep="first").reset_index(drop=True)
56
+ group_dfs.append(group_df)
58
57
 
59
- return df
58
+ return group_dfs
60
59
 
61
60
 
62
61
  def _get_at_att_str(dataset: Dict[str, Any]) -> Dict[str, Any]:
@@ -76,11 +75,34 @@ def _parse_structure_specific_data(
76
75
  # Structure Specific Series
77
76
  df = _reading_str_series(dataset)
78
77
  if GROUP in dataset:
79
- df_group = _reading_group_data(dataset)
80
- common_columns = list(
81
- set(df.columns).intersection(set(df_group.columns))
82
- )
83
- df = pd.merge(df, df_group, on=common_columns, how="left")
78
+ group_dfs = _reading_group_data(dataset)
79
+ original_columns = df.columns.tolist()
80
+ for group_df in group_dfs:
81
+ # Find non-NaN columns in this group
82
+ non_nan_cols = [
83
+ col
84
+ for col in group_df.columns
85
+ if not group_df[col].isna().all()
86
+ ]
87
+
88
+ # Merge keys are intersection of original and non-NaN cols
89
+ merge_cols = list(
90
+ set(original_columns).intersection(set(non_nan_cols))
91
+ )
92
+
93
+ group_df = group_df.drop_duplicates(merge_cols, keep="first")
94
+ df = pd.merge(
95
+ df,
96
+ group_df,
97
+ on=merge_cols,
98
+ how="left",
99
+ suffixes=("", "_drop"),
100
+ )
101
+ for col in list(df.columns):
102
+ if col.endswith("_drop"):
103
+ original = col[:-5]
104
+ df[original] = df[original].fillna(df[col])
105
+ df.drop(col, axis=1, inplace=True)
84
106
  elif OBS in dataset:
85
107
  dataset[OBS] = add_list(dataset[OBS])
86
108
  # Structure Specific All dimensions
@@ -1,9 +1,10 @@
1
1
  """Parsers for reading metadata."""
2
2
 
3
3
  from datetime import datetime
4
- from typing import Any, Callable, Dict, List, Optional, Sequence, Union
4
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Type, Union
5
5
 
6
6
  from msgspec import Struct
7
+ from msgspec.structs import asdict
7
8
 
8
9
  from pysdmx.io.xml.__tokens import (
9
10
  AGENCIES,
@@ -29,6 +30,8 @@ from pysdmx.io.xml.__tokens import (
29
30
  CLS,
30
31
  CODE,
31
32
  CODES_LOW,
33
+ COMPONENT_MAP,
34
+ COMPONENT_MAPS,
32
35
  COMPS,
33
36
  CON,
34
37
  CON_ID,
@@ -44,6 +47,7 @@ from pysdmx.io.xml.__tokens import (
44
47
  CUSTOM_TYPE_SCHEMES,
45
48
  CUSTOM_TYPES,
46
49
  DATA_PROV,
50
+ DATE_PATTERN_MAP,
47
51
  DEPARTMENT,
48
52
  DESC,
49
53
  DFW,
@@ -64,6 +68,8 @@ from pysdmx.io.xml.__tokens import (
64
68
  FACETS,
65
69
  FAX,
66
70
  FAXES,
71
+ FIXED_VALUE_MAP,
72
+ FIXED_VALUE_MAPS,
67
73
  GROUP,
68
74
  GROUP_DIM,
69
75
  GROUPS_LOW,
@@ -98,6 +104,8 @@ from pysdmx.io.xml.__tokens import (
98
104
  PROV_AGREEMENT,
99
105
  PROV_AGREEMENTS,
100
106
  REF,
107
+ REPRESENTATION_MAP,
108
+ REPRESENTATION_MAPS,
101
109
  REQUIRED,
102
110
  ROLE,
103
111
  RULE,
@@ -110,6 +118,8 @@ from pysdmx.io.xml.__tokens import (
110
118
  STR_URL_LOW,
111
119
  STR_USAGE,
112
120
  STRUCTURE,
121
+ STRUCTURE_MAP,
122
+ STRUCTURE_MAPS,
113
123
  TELEPHONE,
114
124
  TELEPHONES,
115
125
  TEXT,
@@ -152,10 +162,19 @@ from pysdmx.model import (
152
162
  AgencyScheme,
153
163
  Code,
154
164
  Codelist,
165
+ ComponentMap,
155
166
  Concept,
156
167
  ConceptScheme,
157
168
  DataType,
169
+ DatePatternMap,
158
170
  Facets,
171
+ FixedValueMap,
172
+ ImplicitComponentMap,
173
+ MultiComponentMap,
174
+ MultiValueMap,
175
+ RepresentationMap,
176
+ StructureMap,
177
+ ValueMap,
159
178
  VtlCodelistMapping,
160
179
  VtlConceptMapping,
161
180
  )
@@ -207,6 +226,11 @@ STRUCTURES_MAPPING = {
207
226
  UDO_SCHEME: UserDefinedOperatorScheme,
208
227
  TRANS_SCHEME: TransformationScheme,
209
228
  VTL_MAPPING_SCHEME: VtlMappingScheme,
229
+ STRUCTURE_MAP: StructureMap,
230
+ COMPONENT_MAP: ComponentMap,
231
+ FIXED_VALUE_MAP: FixedValueMap,
232
+ DATE_PATTERN_MAP: DatePatternMap,
233
+ REPRESENTATION_MAP: RepresentationMap,
210
234
  NAME_PER_SCHEME: NamePersonalisationScheme,
211
235
  CUSTOM_TYPE_SCHEME: CustomTypeScheme,
212
236
  PROV_AGREEMENTS: ProvisionAgreement,
@@ -308,6 +332,10 @@ class StructureParser(Struct):
308
332
  rulesets: Dict[str, RulesetScheme] = {}
309
333
  udos: Dict[str, UserDefinedOperatorScheme] = {}
310
334
  vtl_mappings: Dict[str, VtlMappingScheme] = {}
335
+ structure_maps: Dict[str, StructureMap] = {}
336
+ component_maps: Dict[str, ComponentMap] = {}
337
+ fixed_value_maps: Dict[str, FixedValueMap] = {}
338
+ representation_maps: Dict[str, RepresentationMap] = {}
311
339
  name_personalisations: Dict[str, NamePersonalisationScheme] = {}
312
340
  custom_types: Dict[str, CustomTypeScheme] = {}
313
341
  transformations: Dict[str, TransformationScheme] = {}
@@ -529,16 +557,11 @@ class StructureParser(Struct):
529
557
  if FACETS.lower() in rep:
530
558
  representation_info[LOCAL_FACETS_LOW] = rep.pop(FACETS.lower())
531
559
 
532
- def __format_con_id(self, concept_ref: Dict[str, Any]) -> Dict[str, Any]:
533
- rep = {}
560
+ def __format_con_id(
561
+ self, concept_ref: Union[str, Dict[str, Any]]
562
+ ) -> Dict[str, Any]:
534
563
  if isinstance(concept_ref, str):
535
564
  item_reference = parse_urn(concept_ref)
536
- scheme_reference = Reference(
537
- sdmx_type=CS,
538
- agency=item_reference.agency,
539
- id=item_reference.id,
540
- version=item_reference.version,
541
- )
542
565
  else:
543
566
  item_reference = ItemReference(
544
567
  sdmx_type=concept_ref[CLASS],
@@ -547,27 +570,44 @@ class StructureParser(Struct):
547
570
  version=concept_ref[PAR_VER],
548
571
  item_id=concept_ref[ID],
549
572
  )
550
- scheme_reference = Reference(
551
- sdmx_type=CS,
552
- agency=concept_ref[AGENCY_ID],
553
- id=concept_ref[PAR_ID],
554
- version=concept_ref[PAR_VER],
555
- )
573
+ scheme_reference = Reference(
574
+ sdmx_type=CS,
575
+ agency=item_reference.agency,
576
+ id=item_reference.id,
577
+ version=item_reference.version,
578
+ )
556
579
 
557
580
  concept_scheme = self.concepts.get(str(scheme_reference))
581
+
558
582
  if concept_scheme is None:
559
583
  return {CON: item_reference}
584
+
585
+ short_urn = str(item_reference)
560
586
  for con in concept_scheme.concepts:
561
- if isinstance(concept_ref, str):
562
- if con.id == item_reference.item_id:
563
- rep[CON] = parse_urn(concept_ref)
564
- break
565
- elif con.id == concept_ref[ID]:
566
- rep[CON] = con
567
- break
568
- if CON not in rep:
569
- return {CON: item_reference}
570
- return rep
587
+ con_short = str(
588
+ ItemReference(
589
+ sdmx_type=item_reference.sdmx_type,
590
+ agency=item_reference.agency,
591
+ id=item_reference.id,
592
+ version=item_reference.version,
593
+ item_id=con.id,
594
+ )
595
+ )
596
+
597
+ if con_short == short_urn:
598
+ if con.urn is None:
599
+ con = Concept(
600
+ **{
601
+ **asdict(con),
602
+ "urn": (
603
+ "urn:sdmx:org.sdmx.infomodel.conceptscheme."
604
+ f"{short_urn}"
605
+ ),
606
+ }
607
+ )
608
+ return {CON: con}
609
+
610
+ return {CON: item_reference}
571
611
 
572
612
  @staticmethod
573
613
  def __get_attachment_level( # noqa: C901
@@ -751,6 +791,11 @@ class StructureParser(Struct):
751
791
  del comp[ATT_REL]
752
792
 
753
793
  if ME_REL in comp:
794
+ measures = add_list(comp[ME_REL][MSR])
795
+ if len(measures) == 1 and measures[0] == "OBS_VALUE":
796
+ comp[ATT_LVL] = "O"
797
+ else:
798
+ comp[ATT_LVL] = ",".join(measures)
754
799
  del comp[ME_REL]
755
800
 
756
801
  if AS_STATUS in comp or USAGE in comp:
@@ -762,14 +807,10 @@ class StructureParser(Struct):
762
807
  comp[REQUIRED] = False
763
808
  del comp[status_key]
764
809
 
765
- if "position" in comp:
766
- del comp["position"]
767
-
768
- if ANNOTATIONS in comp:
769
- del comp[ANNOTATIONS]
770
-
771
- if CON_ROLE in comp:
772
- del comp[CON_ROLE]
810
+ unwanted_keys = ["position", ANNOTATIONS, CON_ROLE]
811
+ for key in unwanted_keys:
812
+ if key in comp:
813
+ del comp[key]
773
814
 
774
815
  return Component(**comp)
775
816
 
@@ -1035,6 +1076,124 @@ class StructureParser(Struct):
1035
1076
  )
1036
1077
  return json_elem
1037
1078
 
1079
+ def __build_component_map(
1080
+ self, child_dict: Dict[str, Any]
1081
+ ) -> Union[ComponentMap, MultiComponentMap, ImplicitComponentMap]:
1082
+ if "values" not in child_dict:
1083
+ return ImplicitComponentMap(
1084
+ source=child_dict["source"],
1085
+ target=child_dict["target"],
1086
+ )
1087
+
1088
+ src_list = add_list(child_dict.get("source"))
1089
+ tgt_list = add_list(child_dict.get("target"))
1090
+
1091
+ if len(src_list) != 1 or len(tgt_list) != 1:
1092
+ return MultiComponentMap(
1093
+ source=src_list,
1094
+ target=tgt_list,
1095
+ values=child_dict["values"],
1096
+ )
1097
+
1098
+ return ComponentMap(
1099
+ source=src_list[0],
1100
+ target=tgt_list[0],
1101
+ values=child_dict["values"],
1102
+ )
1103
+
1104
+ def __build_representation_mapping(
1105
+ self, child_dict: Dict[str, Any]
1106
+ ) -> Union[ValueMap, MultiValueMap]:
1107
+ src = child_dict.get("source")
1108
+ tgt = child_dict.get("target")
1109
+
1110
+ src_list = add_list(src) if src is not None else []
1111
+ tgt_list = add_list(tgt) if tgt is not None else []
1112
+
1113
+ if len(src_list) != 1 or len(tgt_list) != 1:
1114
+ return MultiValueMap(
1115
+ source=src_list,
1116
+ target=tgt_list,
1117
+ valid_from=child_dict.get("valid_from"),
1118
+ valid_to=child_dict.get("valid_to"),
1119
+ )
1120
+
1121
+ return ValueMap(
1122
+ source=src_list[0],
1123
+ target=tgt_list[0],
1124
+ valid_from=child_dict.get("valid_from"),
1125
+ valid_to=child_dict.get("valid_to"),
1126
+ )
1127
+
1128
+ def __format_maps(self, element: Dict[str, Any]) -> Dict[str, Any]:
1129
+ if "sourcePattern" in element:
1130
+ element["pattern_type"] = (
1131
+ # DatePatternMap.pattern_type defaults value is fixed
1132
+ "variable" if "FrequencyDimension" in element else "fixed"
1133
+ )
1134
+
1135
+ renames = {
1136
+ "Source": "source",
1137
+ "Target": "target",
1138
+ "Value": "value",
1139
+ "SourceCodelist": "source",
1140
+ "TargetCodelist": "target",
1141
+ "SourceValue": "source",
1142
+ "TargetValue": "target",
1143
+ "RepresentationMap": "values",
1144
+ "sourcePattern": "pattern",
1145
+ "resolvePeriod": "resolve_period",
1146
+ "TargetFrequencyID": "frequency",
1147
+ "FrequencyDimension": "frequency",
1148
+ "validFrom": "valid_from",
1149
+ "validTo": "valid_to",
1150
+ }
1151
+
1152
+ for xml_key, py_key in renames.items():
1153
+ if xml_key in element:
1154
+ element[py_key] = element.pop(xml_key)
1155
+
1156
+ child_class_mapping: Dict[str, Type[Any]] = {
1157
+ "ComponentMap": ComponentMap,
1158
+ "FixedValueMap": FixedValueMap,
1159
+ "DatePatternMap": DatePatternMap,
1160
+ "RepresentationMapping": ValueMap,
1161
+ }
1162
+
1163
+ MapChild = Union[
1164
+ ComponentMap,
1165
+ MultiComponentMap,
1166
+ ImplicitComponentMap,
1167
+ FixedValueMap,
1168
+ DatePatternMap,
1169
+ ValueMap,
1170
+ MultiValueMap,
1171
+ ]
1172
+ consolidated_children: List[MapChild] = []
1173
+
1174
+ for xml_tag, target_class in child_class_mapping.items():
1175
+ if xml_tag not in element:
1176
+ continue
1177
+
1178
+ for child_dict in add_list(element.pop(xml_tag)):
1179
+ self.__format_maps(child_dict)
1180
+
1181
+ if xml_tag == "ComponentMap":
1182
+ consolidated_children.append(
1183
+ self.__build_component_map(child_dict)
1184
+ )
1185
+ elif xml_tag == "RepresentationMapping":
1186
+ consolidated_children.append(
1187
+ self.__build_representation_mapping(child_dict)
1188
+ )
1189
+ else:
1190
+ consolidated_children.append(target_class(**child_dict))
1191
+
1192
+ if consolidated_children:
1193
+ element["maps"] = consolidated_children
1194
+
1195
+ return element
1196
+
1038
1197
  def __format_scheme(
1039
1198
  self, json_elem: Dict[str, Any], scheme: str, item: str
1040
1199
  ) -> Dict[str, ItemScheme]:
@@ -1116,6 +1275,7 @@ class StructureParser(Struct):
1116
1275
  element = self.__format_validity(element)
1117
1276
  element = self.__format_groups(element)
1118
1277
  element = self.__format_components(element)
1278
+ element = self.__format_maps(element)
1119
1279
  if item == PROV_AGREEMENT:
1120
1280
  element = self.__format_prov_agreement(element)
1121
1281
 
@@ -1300,6 +1460,34 @@ class StructureParser(Struct):
1300
1460
  ),
1301
1461
  "transformations",
1302
1462
  ),
1463
+ STRUCTURE_MAPS: process_structure(
1464
+ STRUCTURE_MAPS,
1465
+ lambda data: self.__format_schema(
1466
+ data, STRUCTURE_MAP, STRUCTURE_MAP
1467
+ ),
1468
+ "structure_maps",
1469
+ ),
1470
+ COMPONENT_MAPS: process_structure(
1471
+ COMPONENT_MAPS,
1472
+ lambda data: self.__format_schema(
1473
+ data, COMPONENT_MAP, COMPONENT_MAP
1474
+ ),
1475
+ "component_maps",
1476
+ ),
1477
+ FIXED_VALUE_MAPS: process_structure(
1478
+ FIXED_VALUE_MAPS,
1479
+ lambda data: self.__format_schema(
1480
+ data, FIXED_VALUE_MAP, FIXED_VALUE_MAP
1481
+ ),
1482
+ "fixed_value_maps",
1483
+ ),
1484
+ REPRESENTATION_MAPS: process_structure(
1485
+ REPRESENTATION_MAPS,
1486
+ lambda data: self.__format_schema(
1487
+ data, REPRESENTATION_MAP, REPRESENTATION_MAP
1488
+ ),
1489
+ "representation_maps",
1490
+ ),
1303
1491
  TRANS_SCHEMES: process_structure(
1304
1492
  TRANS_SCHEMES,
1305
1493
  lambda data: self.__format_scheme(