# Class Hierarchy Conversion CleverRDFLib provides a conversion system that allows you to transform the class hierarchy into custom object representations using the Visitor pattern. This is useful when you need to convert the framework's internal representation to a format more suitable for your application. ## Purpose The conversion system enables: - **Custom representations**: Convert to application-specific object structures - **Format transformation**: Transform to different data formats (JSON, XML, custom classes) - **Selective conversion**: Convert only the elements you need - **Relationship preservation**: Maintain parent-child and property relationships ## Conversion Architecture The conversion system uses the Visitor pattern: - **`ClassHierarchyConverter`**: Interface defining conversion operations - **`ClassHierarchyConverterVisitor`**: Visitor that traverses the hierarchy and calls converter methods - **Your implementation**: Custom converter that builds your target representation ## Creating a Custom Converter To create a custom converter, implement the `ClassHierarchyConverter` interface: ```python from cleverrdf_lib.core.interfaces.interfaces import ClassHierarchyConverter from cleverrdf_lib.core.class_hierarchy import PropertyType class MyCustomConverter(ClassHierarchyConverter): """Custom converter that converts to a dictionary representation.""" def __init__(self): self.classes = {} self.properties = {} self.datatypes = {} def build_class(self, iri: str, label: str, comment: str): """Build a class in the target representation.""" class_obj = { "iri": iri, "label": label, "comment": comment, "parents": [], "children": [], "object_properties": [], "datatype_properties": [] } self.classes[iri] = class_obj return class_obj def build_object_property( self, ptype: PropertyType, iri: str, type_iri: str, label: str, comment: str ): """Build an object property in the target representation.""" prop_obj = { "iri": iri, "type": ptype.value, "type_iri": type_iri, "label": label, "comment": comment, "domain": None, "range": None } self.properties[iri] = prop_obj return prop_obj def build_datatype_property( self, ptype: PropertyType, iri: str, type_iri: str, label: str, comment: str ): """Build a datatype property in the target representation.""" prop_obj = { "iri": iri, "type": ptype.value, "type_iri": type_iri, "label": label, "comment": comment, "domain": None, "range": None } self.properties[iri] = prop_obj return prop_obj def build_datatype(self, iri: str, label: str, comment: str): """Build a datatype in the target representation.""" dt_obj = { "iri": iri, "label": label, "comment": comment } self.datatypes[iri] = dt_obj return dt_obj def add_parent_class(self, child, parent): """Add a parent-child relationship.""" if child and parent: child["parents"].append(parent["iri"]) parent["children"].append(child["iri"]) def add_child_class(self, parent, child): """Add a parent-child relationship (alternative method).""" if parent and child: parent["children"].append(child["iri"]) child["parents"].append(parent["iri"]) def assign_object_property(self, property_obj, domain_class, range_class): """Assign domain and range to an object property.""" if property_obj and domain_class: property_obj["domain"] = domain_class["iri"] if property_obj and range_class: property_obj["range"] = range_class["iri"] def assign_datatype_property(self, property_obj, domain_class, range_datatype): """Assign domain and range to a datatype property.""" if property_obj and domain_class: property_obj["domain"] = domain_class["iri"] if property_obj and range_datatype: property_obj["range"] = range_datatype["iri"] def add_object_property_domain(self, domain_class, property_obj): """Add an object property to a class's domain properties.""" if domain_class and property_obj: domain_class["object_properties"].append(property_obj["iri"]) def add_object_property_range(self, range_class, property_obj): """Add an object property to a class's range properties.""" if range_class and property_obj: range_class["object_properties"].append(property_obj["iri"]) def add_datatype_property_domain(self, domain_class, property_obj): """Add a datatype property to a class's domain properties.""" if domain_class and property_obj: domain_class["datatype_properties"].append(property_obj["iri"]) ``` ## Using the Converter Once you have a custom converter, use it with the `ClassHierarchyConverterVisitor`: ```python from cleverrdf_lib.core.class_hierarchy_converter_visitor import ClassHierarchyConverterVisitor from cleverrdf_lib import OntologyLoader # Load ontology and get hierarchy loader = OntologyLoader() result = loader.load_ontology("ontology.ttl") hierarchy = result.class_hierarchy # Create converter converter = MyCustomConverter() # Create visitor visitor = ClassHierarchyConverterVisitor(converter) # Convert the hierarchy visitor.visit_classes_from_the_root(hierarchy) # Access converted data print("Converted classes:") for iri, class_obj in converter.classes.items(): print(f" {iri}: {class_obj}") print("Converted properties:") for iri, prop_obj in converter.properties.items(): print(f" {iri}: {prop_obj}") ``` ## Conversion Process The conversion process follows these steps: 1. **Visitor creation**: Create a `ClassHierarchyConverterVisitor` with your converter 2. **Traversal**: Call `visit_classes_from_the_root()` to start conversion 3. **Class conversion**: Classes are converted starting from root classes 4. **Relationship building**: Parent-child relationships are established 5. **Property conversion**: Properties are converted and linked to classes 6. **Datatype conversion**: Datatypes are converted and linked to properties ### Conversion Order The visitor ensures proper conversion order: 1. **Root classes first**: Root classes are visited first 2. **Parent-child traversal**: Parents and children are visited recursively 3. **Property linking**: Properties are linked after classes are converted 4. **Datatype linking**: Datatypes are linked to datatype properties ## Selective Conversion !!! warning "Selective Conversion Limitations" Returning `None` from converter methods will raise exceptions if other elements of the same type have already been converted, or if relationships need to be established with converted elements. The visitor enforces consistency: you cannot selectively skip some elements while converting others of the same type. If you need selective conversion, you have two options: **Option 1: Convert all elements but mark skipped ones** ```python from cleverrdf_lib.core.interfaces.interfaces import ClassHierarchyConverter class SelectiveConverter(ClassHierarchyConverter): """Converter that marks some classes as skipped.""" def __init__(self, allowed_namespaces: set[str]): self.allowed_namespaces = allowed_namespaces self.classes = {} self.skipped_classes = set() def build_class(self, iri: str, label: str, comment: str): """Convert all classes, but mark some as skipped.""" if not any(iri.startswith(ns) for ns in self.allowed_namespaces): # Mark as skipped but still create the object class_obj = {"iri": iri, "label": label, "comment": comment, "skipped": True} self.skipped_classes.add(iri) else: class_obj = {"iri": iri, "label": label, "comment": comment, "skipped": False} self.classes[iri] = class_obj return class_obj # Always return an object # ... implement other required methods ... ``` **Option 2: Filter before conversion** Filter the hierarchy before conversion to only include elements you want to convert: ```python from cleverrdf_lib.core.class_hierarchy_converter_visitor import ClassHierarchyConverterVisitor from cleverrdf_lib.core.interfaces.interfaces import ClassHierarchyConverter # Assuming MyCustomConverter is defined elsewhere converter = MyCustomConverter() visitor = ClassHierarchyConverterVisitor(converter) # Filter classes before conversion allowed_namespaces = {"http://example.org/allowed/"} # Only convert classes from allowed namespaces (iterate over dictionary values) classes_to_convert = [ cls for cls in hierarchy.get_all_classes().values() if any(cls.iri.startswith(ns) for ns in allowed_namespaces) ] # Then convert only the filtered classes for class_node in classes_to_convert: visitor.visit_class(class_node) ``` ## Conversion to JSON Example converter that produces JSON-serializable dictionaries: ```python import json from cleverrdf_lib.core.interfaces.interfaces import ClassHierarchyConverter from cleverrdf_lib.core.class_hierarchy_converter_visitor import ClassHierarchyConverterVisitor class JSONConverter(ClassHierarchyConverter): """Converter that produces JSON-serializable output.""" def __init__(self): self.output = { "classes": {}, "properties": {}, "datatypes": {} } def build_class(self, iri: str, label: str, comment: str): class_obj = { "iri": iri, "label": label, "comment": comment, "parents": [], "children": [] } self.output["classes"][iri] = class_obj return class_obj # ... implement other methods ... def to_json(self) -> str: """Convert to JSON string.""" return json.dumps(self.output, indent=2) # Use the converter converter = JSONConverter() visitor = ClassHierarchyConverterVisitor(converter) visitor.visit_classes_from_the_root(hierarchy) # Get JSON output json_output = converter.to_json() print(json_output) ``` ## Conversion to Custom Classes Example converter that creates custom class instances: ```python from cleverrdf_lib.core.interfaces.interfaces import ClassHierarchyConverter from cleverrdf_lib.core.class_hierarchy import PropertyType class MyClass: """Custom class representation.""" def __init__(self, iri: str, label: str, comment: str): self.iri = iri self.label = label self.comment = comment self.parents = [] self.children = [] self.properties = [] class MyProperty: """Custom property representation.""" def __init__(self, iri: str, ptype: str): self.iri = iri self.type = ptype self.domain = None self.range = None class CustomClassConverter(ClassHierarchyConverter): """Converter that creates custom class instances.""" def __init__(self): self.classes = {} self.properties = {} def build_class(self, iri: str, label: str, comment: str): class_obj = MyClass(iri, label, comment) self.classes[iri] = class_obj return class_obj def build_object_property( self, ptype: PropertyType, iri: str, type_iri: str, label: str, comment: str ): prop_obj = MyProperty(iri, ptype.value) self.properties[iri] = prop_obj return prop_obj def build_datatype_property( self, ptype: PropertyType, iri: str, type_iri: str, label: str, comment: str ): prop_obj = MyProperty(iri, ptype.value) self.properties[iri] = prop_obj return prop_obj def assign_object_property(self, property_obj, domain_class, range_class): """Assign domain and range to an object property.""" if property_obj and domain_class: property_obj.domain = domain_class if property_obj and range_class: property_obj.range = range_class def assign_datatype_property(self, property_obj, domain_class, range_datatype): """Assign domain and range to a datatype property.""" if property_obj and domain_class: property_obj.domain = domain_class if property_obj and range_datatype: property_obj.range = range_datatype # ... implement other methods ... ``` ## Error Handling The conversion process can raise exceptions: - **`ClassConversionException`**: Raised when class conversion fails - **`PropertyConversionException`**: Raised when property conversion fails - **`DataTypeConversionException`**: Raised when datatype conversion fails - **`InconsistentClassHierarchyException`**: Raised when inconsistencies are detected ```python from cleverrdf_lib.core.exceptions import ( ClassConversionException, PropertyConversionException ) try: visitor.visit_classes_from_the_root(hierarchy) except ClassConversionException as e: print(f"Class conversion failed: {e}") except PropertyConversionException as e: print(f"Property conversion failed: {e}") ``` ## Best Practices 1. **Implement all methods**: Implement all required methods of `ClassHierarchyConverter` 2. **Handle None values**: Converter methods can return `None` to skip conversion 3. **Preserve relationships**: Ensure parent-child and property relationships are maintained 4. **Validate consistency**: Check for inconsistencies during conversion 5. **Test thoroughly**: Test converters with various ontology structures ## Converter Interface The `ClassHierarchyConverter` interface requires these methods: - `build_class(iri, label, comment)`: Build a class - `build_object_property(ptype, iri, type_iri, label, comment)`: Build an object property - `build_datatype_property(ptype, iri, type_iri, label, comment)`: Build a datatype property - `build_datatype(iri, label, comment)`: Build a datatype - `add_parent_class(child, parent)`: Add parent-child relationship - `add_child_class(parent, child)`: Add parent-child relationship - `assign_object_property(property, domain, range)`: Assign domain and range to an object property - `assign_datatype_property(property, domain, range)`: Assign domain and range to a datatype property - `add_object_property_domain(domain, property)`: Add object property to domain - `add_object_property_range(range, property)`: Add object property to range - `add_datatype_property_domain(domain, property)`: Add datatype property to domain ## Next Steps - Learn about [Class Hierarchy Navigation](class_hierarchy.md) - Explore [Loading Result API](loading_result.md) - Understand [Observers API](observers.md)