Serialization
Every kfnetlist type supports two serialization formats:
| Method | Format | Use case |
|---|---|---|
to_json() / from_json() |
JSON string | File I/O, network transport |
to_dict() / from_dict() |
Python dict | In-memory manipulation, Pydantic integration |
Both are fully round-trippable: T.from_json(obj.to_json()) == obj.
import json
from kfnetlist import (
Net,
Netlist,
NetlistInstance,
NetlistPort,
PortArrayRef,
PortRef,
)
Individual type serialization
Each port type, net, and instance can be serialized independently.
NetlistPort
p = NetlistPort(name="in")
print("JSON:", p.to_json())
print("Dict:", p.to_dict())
JSON: {"name":"in"}
Dict: {'name': 'in'}
PortRef
ref = PortRef(instance="wg1", port="o2")
print("JSON:", ref.to_json())
print("Dict:", ref.to_dict())
JSON: {"instance":"wg1","port":"o2"}
Dict: {'instance': 'wg1', 'port': 'o2'}
PortArrayRef
aref = PortArrayRef(instance="pad_array", port="p1", ia=2, ib=0)
print("JSON:", aref.to_json())
print("Dict:", aref.to_dict())
JSON: {"instance":"pad_array","port":"p1","ia":2,"ib":0}
Dict: {'instance': 'pad_array', 'port': 'p1', 'ia': 2, 'ib': 0}
Net
net = Net(
[
NetlistPort(name="in"),
PortRef(instance="wg1", port="o1"),
]
)
print("JSON:", net.to_json())
print("Dict:", net.to_dict())
JSON: [{"name":"in"},{"instance":"wg1","port":"o1"}]
Dict: [{'name': 'in'}, {'instance': 'wg1', 'port': 'o1'}]
NetlistInstance
inst = NetlistInstance(
kcl="MY_PDK",
component="straight",
settings={"width": 500, "length": 10_000},
name="wg1",
)
print("JSON:", inst.to_json())
print("Dict:", inst.to_dict())
JSON: {"kcl":"MY_PDK","component":"straight","settings":{"length":10000,"width":500}}
Dict: {'kcl': 'MY_PDK', 'component': 'straight', 'settings': {'length': 10000, 'width': 500}}
Full Netlist serialization
The Netlist itself supports JSON and dict round-trips containing all
instances, nets, and ports.
nl = Netlist()
nl.create_inst("wg1", kcl="PDK", component="straight", settings={"width": 500})
nl.create_inst("wg2", kcl="PDK", component="straight", settings={"width": 500})
p_in = nl.create_port("in")
nl.create_net(p_in, PortRef(instance="wg1", port="o1"))
nl.create_net(PortRef(instance="wg1", port="o2"), PortRef(instance="wg2", port="o1"))
nl.sort()
json_str = nl.to_json()
print(json.dumps(json.loads(json_str), indent=2))
{
"instances": {
"wg1": {
"kcl": "PDK",
"component": "straight",
"settings": {
"width": 500
},
"array": {
"na": 1,
"nb": 1
}
},
"wg2": {
"kcl": "PDK",
"component": "straight",
"settings": {
"width": 500
},
"array": {
"na": 1,
"nb": 1
}
}
},
"nets": [
[
{
"name": "in"
},
{
"instance": "wg1",
"port": "o1"
}
],
[
{
"instance": "wg1",
"port": "o2"
},
{
"instance": "wg2",
"port": "o1"
}
]
],
"ports": [
{
"name": "in"
}
]
}
JSON round-trip
nl_restored = Netlist.from_json(json_str)
nl_restored.sort()
assert nl.to_dict() == nl_restored.to_dict()
print("JSON round-trip ✓")
JSON round-trip ✓
Dict round-trip
d = nl.to_dict()
nl_from_dict = Netlist.from_dict(d)
nl_from_dict.sort()
assert nl.to_dict() == nl_from_dict.to_dict()
print("Dict round-trip ✓")
Dict round-trip ✓
Wire format notes
The JSON/dict wire format has a few conventions:
- Instance names are keys, not stored inside the instance payload. This
avoids redundancy (
{"wg1": {"kcl": ..., "component": ...}}). - Net members are untagged — the deserializer infers the type from the
fields present (
{"name": ...}→NetlistPort,{"instance": ..., "port": ...}→PortRef, addia/ib→PortArrayRef). - Nets auto-sort their members on construction and mutation, so the
serialized order is always deterministic after
sort().
Pydantic integration
All kfnetlist types implement __get_pydantic_core_schema__, so they can be
used directly as fields in Pydantic v2 models without custom validators.
from pydantic import BaseModel
from kfnetlist import Netlist
class Design(BaseModel):
name: str
netlist: Netlist
See Also
| Topic | Where |
|---|---|
| Netlist data model | Netlist Model |
| Port types | Ports & Refs |