From 89134a6b9a71f5c3046639fc255d3cbf8868fec9 Mon Sep 17 00:00:00 2001 From: Richard Eckart de Castilho Date: Mon, 23 Jan 2023 18:20:01 +0100 Subject: [PATCH] #265 - Error parsing certain JSONs with embedded type system - First load types then features to resolve recursive type definitions --- cassis/json.py | 8 +++++++- tests/test_json.py | 21 ++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/cassis/json.py b/cassis/json.py index 2b6651a..bfae1f5 100644 --- a/cassis/json.py +++ b/cassis/json.py @@ -76,8 +76,12 @@ def deserialize(self, source: Union[IO, str], typesystem: Optional[TypeSystem] = embedded_typesystem = TypeSystem() json_typesystem = data.get(TYPES_FIELD) + # First load all the types but no features since features of a type X might be of a later loaded type Y for type_name, json_type in json_typesystem.items(): self._parse_type(embedded_typesystem, type_name, json_type) + # Now we are sure we know all the types, we can create the features + for type_name, json_type in json_typesystem.items(): + self._parse_features(embedded_typesystem, type_name, json_type) typesystem = merge_typesystems(typesystem, embedded_typesystem) @@ -123,8 +127,10 @@ def deserialize(self, source: Union[IO, str], typesystem: Optional[TypeSystem] = def _parse_type(self, typesystem: TypeSystem, type_name: str, json_type: Dict[str, any]): super_type_name = json_type[SUPER_TYPE_FIELD] description = json_type.get(DESCRIPTION_FIELD) - new_type = typesystem.create_type(type_name, super_type_name, description=description) + typesystem.create_type(type_name, super_type_name, description=description) + def _parse_features(self, typesystem: TypeSystem, type_name: str, json_type: Dict[str, any]): + new_type = typesystem.get_type(type_name) for key, json_feature in json_type.items(): if key.startswith(RESERVED_FIELD_PREFIX): continue diff --git a/tests/test_json.py b/tests/test_json.py index 3889b11..b2a5419 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -1,6 +1,6 @@ import json -from cassis.typesystem import TYPE_NAME_ANNOTATION +from cassis.typesystem import TYPE_NAME_ANNOTATION, TypeSystemMode from tests.fixtures import * from tests.test_files.test_cas_generators import MultiFeatureRandomCasGenerator, MultiTypeRandomCasGenerator from tests.util import assert_json_equal @@ -152,3 +152,22 @@ def test_unicode(json_path, annotations): expected_utf8_bytes = expected[4] actual_utf8_bytes = bytes(actual_covered_text, "UTF-8") assert actual_utf8_bytes == expected_utf8_bytes + +def test_recursive_type_system(): + typesystem = TypeSystem() + type_a = typesystem.create_type(name='example.TypeA') + type_b = typesystem.create_type(name='example.TypeB') + typesystem.create_feature(domainType=type_a, name='typeB', rangeType=type_b) + typesystem.create_feature(domainType=type_b, name='typeA', rangeType=type_a) + + source_cas = Cas(typesystem=typesystem) + target_cas = load_cas_from_json(source_cas.to_json(type_system_mode=TypeSystemMode.FULL)) + + target_type_a = target_cas.typesystem.get_type('example.TypeA') + target_type_b = target_cas.typesystem.get_type('example.TypeB') + + # We have to compare types by name below due to https://github.com/dkpro/dkpro-cassis/issues/270 + assert target_type_a is not None + assert target_type_a.get_feature('typeB').rangeType.name == target_type_b.name + assert target_type_b is not None + assert target_type_b.get_feature('typeA').rangeType.name == target_type_a.name