Serialization¶
This page describes how infrasys serializes a system and its components to JSON when a user calls
System.to_json() and System.from_json().
Components¶
infrasys converts its nested dictionaries of components-by-type into a flat array. Each component
records metadata about its actual Python type into a field called __metadata__. Here is an example
of a serialized Location object. Note that it includes the module and type. infrasys uses this
information during de-serialization to dynamically import the type and construct it. This allows
serialization to work with types defined outside of infrasys as long as the user has imported
those types.
{
"uuid": "1e5f90ae-a386-4c8a-89ae-0ed123da3e26",
"name": null,
"x": 0.0,
"y": 0.0,
"crs": null,
"__metadata__": {
"fields": {
"module": "infrasys.location",
"type": "Location",
"serialized_type": "base"
}
}
},
Composed components¶
There are many cases where one component will contain an instance of another component. For example,
a Bus may contain a Location or a Generator may contain a Bus. When serializing each
component, infrasys checks the type of each of that component’s fields. If a value is another
component (which means that it must also be attached to system), infrasys replaces that instance
with its UUID. It does this to avoid duplicating data in the JSON file.
Here is an example of a serialized Bus. Note the value for the coordinates field. It contains the
type and UUID of the actual coordinates. During de-serialization, infrasys will detect this
condition and only attempt to de-serialize the bus once all Location instances have been
de-serialized.
{
"uuid": "e503984a-3285-43b6-84c2-805eb3889210",
"name": "bus1",
"voltage": 1.1,
"coordinates": {
"__metadata__": {
"fields": {
"module": "infrasys.location",
"type": "Location",
"serialized_type": "composed_component",
"uuid": "1e5f90ae-a386-4c8a-89ae-0ed123da3e26"
}
}
},
"__type_metadata__": {
"fields": {
"module": "tests.models.simple_system",
"type": "SimpleBus",
"serialized_type": "base"
}
}
},
Denormalized component data¶
There are cases where users may prefer to have the full, denormalized JSON data for a component.
All components are of type pydantic.BaseModel and so implement the method model_dump_json.
Here is an example of a bus serialized that way (bus.model_dump_json(indent=2)):
{
"uuid": "e503984a-3285-43b6-84c2-805eb3889210",
"name": "bus1",
"voltage": 1.1,
"coordinates": {
"uuid": "1e5f90ae-a386-4c8a-89ae-0ed123da3e26",
"name": null,
"x": 0.0,
"y": 0.0,
"crs": null
}
}
Pint Quantities¶
infrasys encodes metadata into component JSON when that component contains a pint.Quantity
instance. Here is an example of such a component:
{
"uuid": "711d2724-5814-4e0e-be5f-4b0b825b7f07",
"name": "test",
"distance": {
"value": 2,
"units": "meter",
"__metadata__": {
"fields": {
"module": "infrasys.quantities",
"type": "Distance",
"serialized_type": "quantity"
}
}
},
"__metadata__": {
"fields": {
"module": "tests.test_serialization",
"type": "ComponentWithPintQuantity",
"serialized_type": "base"
}
}
}
Time Series¶
If the user stores time series data in Arrow files (default behavior), then infrasys will copy
the Arrow files into the user-specified directory in system.to_json().
If the user instead chose to store time series in memory then infrasys will series that data
into Arrow files in the user-specified directory in system.to_json().