PageSourceSearch

https://lionweb.io/assets/js/a12ea136.a410a184.js

js lionweb.io collected 2026-10-03 23:15:03 UTC 12,630 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunklionweb_python_docs=self.webpackChunklionweb_python_docs||[]).push([[619],{2097:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>_,frontMatter:()=>r,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"Guides-Python/serialization","title":"Serialization in LionWeb Python","description":"The LionWeb Python library provides robust support for serializing and deserializing models composed of nodes.","source":"@site/docs/Guides-Python/serialization.md","sourceDirName":"Guides-Python","slug":"/Guides-Python/serialization","permalink":"/Guides-Python/serialization","draft":false,"unlisted":false,"editUrl":"https://github.com/lionweb-io/LionWeb-io.github.io/tree/main/website/docs/Guides-Python/serialization.md","tags":[],"version":"current","sidebarPosition":43,"frontMatter":{"sidebar_position":43},"sidebar":"tutorialSidebar","previous":{"title":"Creating and Working with Nodes in LionWeb","permalink":"/Guides-Python/working-with-nodes"},"next":{"title":"Working with the LionWeb Repository","permalink":"/Guides-Python/working-with-repository"}}');var a=i(4848),t=i(8453);const r={sidebar_position:43},o="Serialization in LionWeb Python",l={},d=[{value:"Homogeneous Serialization",id:"homogeneous-serialization",level:2},{value:"Custom Deserialization with Heterogeneous Nodes",id:"custom-deserialization-with-heterogeneous-nodes",level:2},{value:"Example",id:"example",level:3},{value:"Serializing Language Definitions",id:"serializing-language-definitions",level:2},{value:"A complete example",id:"a-complete-example",level:2}];function c(e){const n={code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"serialization-in-lionweb-python",children:"Serialization in LionWeb Python"})}),"\n",(0,a.jsxs)(n.p,{children:["The LionWeb Python library provides robust support for ",(0,a.jsx)(n.strong,{children:"serializing"})," and ",(0,a.jsx)(n.strong,{children:"deserializing"})," models composed of nodes.\nThese nodes can represent instances of a language as well as the language definitions themselves."]}),"\n",(0,a.jsx)(n.p,{children:"Serialization is essential to:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Communicate with a LionWeb-compliant repository."}),"\n",(0,a.jsx)(n.li,{children:"Store models on disk or transmit them over the network."}),"\n",(0,a.jsx)(n.li,{children:"Load them back into memory and process them."}),"\n",(0,a.jsx)(n.li,{children:"Maintain compatibility across clients and systems."}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"homogeneous-serialization",children:"Homogeneous Serialization"}),"\n",(0,a.jsxs)(n.p,{children:["When working with the ",(0,a.jsx)(n.strong,{children:"homogeneous API"}),", such as ",(0,a.jsx)(n.code,{children:"DynamicNode"}),", the LionWeb Python library provides default\nserialization mechanisms that can be used out of the box."]}),"\n",(0,a.jsx)(n.p,{children:"The following example demonstrates how to:"}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsxs)(n.li,{children:["Define a small language consisting of ",(0,a.jsx)(n.code,{children:"TaskList"})," and ",(0,a.jsx)(n.code,{children:"Task"})," concepts."]}),"\n",(0,a.jsxs)(n.li,{children:["Create model instances using ",(0,a.jsx)(n.code,{children:"DynamicNode"})," subclasses."]}),"\n",(0,a.jsx)(n.li,{children:"Serialize the model using the standard JSON serializer."}),"\n",(0,a.jsx)(n.li,{children:"Deserialize the JSON back into nodes."}),"\n"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-python",children:"# After creating nodes (see below), serialize them:\nserialization = create_standard_json_serialization()\nserialized = serialization.serialize_tree_to_json_string(task_list)\n\n# Deserialize with DynamicNode support:\nserialization.enable_dynamic_nodes()\ndeserialized1 = root(serialization.deserialize_string_to_nodes(serialized))\n"})}),"\n",(0,a.jsx)(n.p,{children:"If you do not enable dynamic nodes or register deserializers, the deserialization of unknown node types will throw an error."}),"\n",(0,a.jsx)(n.h2,{id:"custom-deserialization-with-heterogeneous-nodes",children:"Custom Deserialization with Heterogeneous Nodes"}),"\n",(0,a.jsxs)(n.p,{children:["When using custom node classes (i.e., heterogeneous nodes), you need to ",(0,a.jsx)(n.strong,{children:"register a custom deserializer"})," per concept to instantiate the correct subclass:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-python",children:"def task_list_deserializer(classifier, sci, nodes_by_id, property_values) -> TaskList:\n    return TaskList(id=sci.id)\n\n\ndef task_deserializer(classifier, sci, nodes_by_id, property_values) -> Task:\n    return Task(id=sci.id, name=property_values[name_property])\n\n\nserialization.instantiator.register_custom_deserializer(task_list_concept.id, task_list_deserializer)\nserialization.instantiator.register_custom_deserializer(task_concept.id, task_deserializer)\n"})}),"\n",(0,a.jsx)(n.p,{children:"This allows you to benefit from type-safe APIs and static checking during development."}),"\n",(0,a.jsx)(n.h3,{id:"example",children:"Example"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-python",children:'# Create a task list and tasks\nerrands = TaskList()\nerrands.add_task(Task("My Task #1"))\nerrands.add_task(Task("My Task #2"))\n\n \n# Validate\nresult = NodeTreeValidator().validate(errands)\nif result.has_errors():\n    raise ValueError(f"The tree is invali
1d: {result}")\n\n# Serialize\nserialization = create_standard_json_serialization()\nserialized = serialization.serialize_tree_to_json_string(task_list)\nprint("== Tasks list ==")\nprint(serialized)\nprint()\n\n# Deserialize\nserialization.enableDynamicNodes() # or register deserializers\ndeserialized1 = root(serialization.deserialize_string_to_nodes(serialized))\n'})}),"\n",(0,a.jsx)(n.h2,{id:"serializing-language-definitions",children:"Serializing Language Definitions"}),"\n",(0,a.jsx)(n.p,{children:"LionWeb Python treats languages as regular node trees:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Concepts, Properties, Containments, and the Language itself are just nodes."}),"\n",(0,a.jsx)(n.li,{children:"The default serializer knows how to instantiate these standard types."}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"So, you can also do:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-python",children:"languageJson = serialization.serialize_tree_to_json_string(myLanguage)\ndeserializedLanguageElements = serialization.deserialize_string_to_nodes(languageJson)\n"})}),"\n",(0,a.jsxs)(n.p,{children:["No special registration is required for built-in language elements like ",(0,a.jsx)(n.code,{children:"Language"}),", ",(0,a.jsx)(n.code,{children:"Concept"}),", etc.\u2014their deserializers are pre-registered in the standard serializer."]}),"\n",(0,a.jsx)(n.h2,{id:"a-complete-example",children:"A complete example"}),"\n",(0,a.jsx)(n.p,{children:"By combining dynamic and custom deserialization strategies, LionWeb Python offers both flexibility and strong typing for working with serialized models and metamodels."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-python",children:'import uuid\nfrom typing import List, Optional\n\nfrom lionweb.language import Language, Concept, Property, Containment, LionCoreBuiltins\nfrom lionweb.model import DynamicNode\nfrom lionweb.serialization import create_standard_json_serialization, InstantiationError\nfrom lionweb.utils import root\nfrom lionweb.utils.node_tree_validator import NodeTreeValidator\n\n# === Define the Language ===\n\n# Global elements\ntask_list_concept: Concept\ntask_concept: Concept\nname_property: Property\ntasks_containment: Containment\ntask_language: Language\n\n\ndef define_language():\n    global task_list_concept, task_concept, name_property, tasks_containment, task_language\n\n    # Define the \'TaskList\' concept\n    task_list_concept = Concept(\n        name="TaskList", key="TaskList", id="TaskList-id", abstract=False, partition=True\n    )\n\n    # Define the \'Task\' concept\n    task_concept = Concept(\n        name="Task", key="Task", id="Task-id", abstract=False, partition=False\n    )\n\n    # Add a \'tasks\' containment\n    tasks_containment = Containment(\n        name="tasks",\n        key="TasksList-tasks",\n        id="TasksList-tasks-id",\n        type=task_concept,\n        multiple=True,\n        optional=False,\n    )\n    task_list_concept.add_feature(tasks_containment)\n\n    # Add a \'name\' property\n    name_property = Property(\n        name="name", key="task-name", id="task-name-id", type=LionCoreBuiltins.get_string()\n    )\n    task_concept.add_feature(name_property)\n\n    # Define the language container\n    task_language = Language(\n        name="Task Language",\n        key="task",\n        id="task-id",\n        version="1.0"\n    )\n    task_language.add_element(task_list_concept)\n    task_language.add_element(task_concept)\n\n\n# === Define specific DynamicNode subclasses ===\n\nclass Task(DynamicNode):\n    def __init__(self, name: str, id: Optional[str] = None):\n        super().__init__(id or str(uuid.uuid4()), task_concept)\n        self.set_name(name)\n\n    def set_name(self, name: str):\n        self.set_property_value(name_property, name)\n\n    def get_name(self) -> str:\n        return self.get_property_value(name_property)\n\n\nclass TaskList(DynamicNode):\n    def __init__(self, id:Optional[str] = None):\n        super().__init__(id or str(uuid.uuid4()), task_list_concept)\n\n    def add_task(self, task: Task):\n        self.add_child(tasks_containment, task)\n\n    def get_tasks(self) -> List[Task]:\n        return self.get_children(tasks_containment)\n\n# === Main logic ===\n\ndef create_task_list() -> TaskList:\n    define_language()\n\n    errands = TaskList()\n    errands.add_task(Task("My Task #1"))\n    errands.add_task(Task("My Task #2"))\n\n    result = NodeTreeValidator().validate(errands)\n    if result.has_errors():\n        raise ValueError(f"The tree is invali
1d: {result}")\n\n    return errands\n\n\nif __name__ == "__main__":\n    task_list = create_task_list()\n    serialization = create_standard_json_serialization()\n\n    # === Serialize\n    serialized = serialization.serialize_tree_to_json_string(task_list)\n    print("== Tasks list ==")\n    print(serialized)\n    print()\n\n    # === Attempt deserialization without dynamic mode\n    try:\n        serialization.deserialize_string_to_nodes(serialized)\n        raise RuntimeError("We expect an exception")\n    except InstantiationError as e:\n        print("Expected error:", e)\n\n    # === First deserialization with dynamic nodes\n    serialization.enable_dynamic_nodes()\n    deserialized1 = root(serialization.deserialize_string_to_nodes(serialized))\n    print("First deserialization - Deserialized as", type(deserialized1).__name__)\n    if type(deserialized1) is not DynamicNode:\n        raise RuntimeError("Deserialized object should be a DynamicNode")\n\n\n    # === Register custom deserializers\n    def task_list_deserializer(classifier, sci, nodes_by_id, property_values) -> TaskList:\n        return TaskList(id=sci.id)\n\n\n    def task_deserializer(classifier, sci, nodes_by_id, property_values) -> Task:\n        return Task(id=sci.id, name=property_values[name_property])\n\n\n    serialization.instantiator.register_custom_deserializer(task_list_concept.id, task_list_deserializer)\n    serialization.instantiator.register_custom_deserializer(task_concept.id, task_deserializer)\n\n    deserialized2 = root(serialization.deserialize_string_to_nodes(serialized))\n    print("Second deserialization - Deserialized as", type(deserialized2).__name__)\n    if type(deserialized2) is not TaskList:\n        raise RuntimeError(f"Deserialized object should be a TaskList while it is {type(deserialized2)}")\n'})})]})}function _(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>o});var s=i(6540);const a={},t=s.createContext(a);function r(e){const n=s.useContext(t);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),s.createElement(t.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.