Extensions
Undocumented JSON fields survive a round-trip verbatim, which is what keeps a hub from shredding vendor data.
OCPI 2.3.0 has a chapter about extensibility, and it asks implementations to tolerate values and
fields they do not know. Most libraries do not. ocpi-kit does, on both axes:
- an unknown enum value keeps its text in a
Customvariant — see Enums - an unknown field lands in an
Extensionsmap on the object it came from
let json = r#"{"id":"LOC1", "…":"…", "acme_grid_operator":"Stedin"}"#;
let location: Location = serde_json::from_str(json)?;
assert_eq!(location.extensions.get("acme_grid_operator").unwrap(), "Stedin");
assert_eq!(serde_json::to_string(&location)?, json); // written back verbatim
Every wire object carries extensions: Extensions, a #[serde(flatten)] map. Round-tripping an
object you do not fully understand is byte-stable.
Why it matters in a hub
A hub sits between many parties. If a CPO sends a Location with a vendor field and the hub decodes it into a strict struct and re-encodes it, the field is gone before it reaches the eMSP that needed it — silently, and nothing fails. A crate that cannot represent the unknown cannot forward it.
Writing extensions of your own
let mut location = Location::builder()./* … */.build();
location.extensions.insert("acme_grid_operator", "Stedin");
The 2.3.0 specification does not define a naming rule for extension fields beyond "do not modify the standard ones, do propose additions". A vendor prefix is this crate's recommendation, not a requirement — it keeps two parties' extensions from colliding in the same object.
What extensions are not
Extensions holds JSON values, not typed data, and it is deliberately not validated. If you find
yourself reading structured data out of it in more than one place, you probably want your own
struct with a #[serde(flatten)] field of your own on top of the OCPI object.