use crate::code_enum::MapKey;
use serde::de::{self, DeserializeOwned, Deserializer, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fmt;
use std::marker::PhantomData;
pub fn deserialize_map_or_vec<'de, D, K, V>(
deserializer: D,
) -> Result<Option<BTreeMap<K, V>>, D::Error>
where
D: Deserializer<'de>,
K: DeserializeOwned + Ord + Serialize,
V: DeserializeOwned,
{
struct MapOrVecVisitor<K, V>(PhantomData<(K, V)>);
impl<'de, K, V> Visitor<'de> for MapOrVecVisitor<K, V>
where
K: DeserializeOwned + Ord + Serialize,
V: DeserializeOwned,
{
type Value = Option<BTreeMap<K, V>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map, an array, or null")
}
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_map<M: MapAccess<'de>>(self, map: M) -> Result<Self::Value, M::Error> {
let val: serde_json::Value =
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
if let Ok(btree) = serde_json::from_value::<BTreeMap<K, V>>(val.clone()) {
return Ok(Some(btree));
}
if let Some(key) = find_key_in_value::<K>(&val) {
let entity: V = serde_json::from_value(val).map_err(de::Error::custom)?;
let mut map = BTreeMap::new();
map.insert(key, entity);
return Ok(Some(map));
}
Err(de::Error::custom(
"map value is neither a keyed BTreeMap nor a single entity with a valid key field",
))
}
fn visit_seq<S: SeqAccess<'de>>(self, mut seq: S) -> Result<Self::Value, S::Error> {
let mut map = BTreeMap::new();
while let Some(val) = seq.next_element::<serde_json::Value>()? {
let key = find_key_in_value::<K>(&val)
.ok_or_else(|| de::Error::custom("array element has no valid map key field"))?;
let entity: V = serde_json::from_value(val).map_err(de::Error::custom)?;
map.insert(key, entity);
}
Ok(Some(map))
}
}
deserializer.deserialize_any(MapOrVecVisitor(PhantomData))
}
pub fn deserialize_keyed_map<'de, D, K, V>(
deserializer: D,
) -> Result<Option<BTreeMap<K, V>>, D::Error>
where
D: Deserializer<'de>,
K: MapKey,
V: DeserializeOwned,
{
let Some(value) = Option::<serde_json::Value>::deserialize(deserializer)? else {
return Ok(None);
};
let mut map = BTreeMap::new();
let mut insert = |key: K, element: serde_json::Value| -> Result<(), D::Error> {
let entity: V = serde_json::from_value(element).map_err(de::Error::custom)?;
let code = key.as_str().to_string();
if map.insert(key, entity).is_some() {
return Err(de::Error::custom(format!(
"duplicate map key `{code}`: two elements carry the same qualifier"
)));
}
Ok(())
};
match value {
serde_json::Value::Null => return Ok(None),
serde_json::Value::Array(items) => {
for element in items {
let key = K::of_element(&element).map_err(de::Error::custom)?;
insert(key, element)?;
}
}
serde_json::Value::Object(obj) => {
let keyed = !obj.is_empty()
&& obj.iter().all(|(k, v)| {
v.is_object() && K::from_code(k).is_some_and(|key| key.as_str() == k)
});
if keyed {
for (k, mut element) in obj {
let key = K::from_code(&k).expect("checked above");
if element.get(K::FIELD).is_some() {
let derived = K::of_element(&element).map_err(de::Error::custom)?;
if derived != key {
return Err(de::Error::custom(format!(
"map key `{k}` disagrees with the element's `{}` (`{}`)",
K::FIELD,
derived.as_str()
)));
}
}
key.fill_element(&mut element);
insert(key, element)?;
}
} else {
let element = serde_json::Value::Object(obj);
let key = K::of_element(&element).map_err(de::Error::custom)?;
insert(key, element)?;
}
}
other => {
return Err(de::Error::custom(format!(
"expected a map, an array or null, got {other}"
)))
}
}
Ok(Some(map))
}
pub fn serialize_keyed_map<S, K, V>(
map: &Option<BTreeMap<K, V>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
K: MapKey,
V: Serialize,
{
use serde::ser::{Error, SerializeSeq};
let Some(map) = map else {
return serializer.serialize_none();
};
if map.len() == 1 {
let (key, value) = map.iter().next().expect("one element");
let mut element = serde_json::to_value(value).map_err(S::Error::custom)?;
key.fill_element(&mut element);
return element.serialize(serializer);
}
let mut seq = serializer.serialize_seq(Some(map.len()))?;
for (key, value) in map {
let mut element = serde_json::to_value(value).map_err(S::Error::custom)?;
key.fill_element(&mut element);
seq.serialize_element(&element)?;
}
seq.end()
}
pub fn serialize_vec_or_single<S, T>(
value: &Option<Vec<T>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
T: Serialize,
{
match value.as_deref() {
None => serializer.serialize_none(),
Some([single]) => single.serialize(serializer),
Some(items) => items.serialize(serializer),
}
}
pub fn deserialize_code_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
match Option::<serde_json::Value>::deserialize(deserializer)? {
None | Some(serde_json::Value::Null) => Ok(None),
Some(serde_json::Value::String(s)) => Ok(Some(s)),
Some(serde_json::Value::Object(obj)) => match obj.get("code") {
Some(serde_json::Value::String(code)) => Ok(Some(code.clone())),
_ => Err(de::Error::custom("code object without a string `code`")),
},
Some(other) => Err(de::Error::custom(format!(
"expected a string or a code object, got {other}"
))),
}
}
pub fn typed_hop<T: DeserializeOwned + Serialize>(
value: serde_json::Value,
) -> Result<serde_json::Value, serde_json::Error> {
serde_json::to_value(serde_json::from_value::<T>(value)?)
}
pub fn deserialize_vec_or_single<'de, D, T>(deserializer: D) -> Result<Option<Vec<T>>, D::Error>
where
D: Deserializer<'de>,
T: DeserializeOwned,
{
struct VecOrSingleVisitor<T>(PhantomData<T>);
impl<'de, T: DeserializeOwned> Visitor<'de> for VecOrSingleVisitor<T> {
type Value = Option<Vec<T>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an array, an object, or null")
}
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_seq<S: SeqAccess<'de>>(self, seq: S) -> Result<Self::Value, S::Error> {
let vec: Vec<T> = Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
Ok(Some(vec))
}
fn visit_map<M: MapAccess<'de>>(self, map: M) -> Result<Self::Value, M::Error> {
let single: T = Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
Ok(Some(vec![single]))
}
}
deserializer.deserialize_any(VecOrSingleVisitor(PhantomData))
}
fn find_key_in_value<K: for<'de> Deserialize<'de>>(val: &serde_json::Value) -> Option<K> {
let obj = val.as_object()?;
for (_field_name, field_val) in obj {
if let Some(s) = field_val.as_str() {
if let Ok(key) = serde_json::from_value::<K>(serde_json::Value::String(s.to_string())) {
return Some(key);
}
}
}
for (_field_name, field_val) in obj {
if let Some(nested_obj) = field_val.as_object() {
for (_nested_name, nested_val) in nested_obj {
if let Some(s) = nested_val.as_str() {
if let Ok(key) =
serde_json::from_value::<K>(serde_json::Value::String(s.to_string()))
{
return Some(key);
}
}
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
enum TestKey {
#[serde(rename = "MS")]
Ms,
#[serde(rename = "MR")]
Mr,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct TestEntity {
pub marktrolle: Option<String>,
pub name: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
struct TestContainer {
#[serde(default, deserialize_with = "deserialize_map_or_vec")]
pub items: Option<BTreeMap<TestKey, TestEntity>>,
}
#[test]
fn test_deserialize_from_array() {
let json = r#"{"items": [
{"marktrolle": "MS", "name": "Sender"},
{"marktrolle": "MR", "name": "Recipient"}
]}"#;
let container: TestContainer = serde_json::from_str(json).unwrap();
let items = container.items.unwrap();
assert_eq!(items.len(), 2);
assert_eq!(items[&TestKey::Ms].name.as_deref(), Some("Sender"));
assert_eq!(items[&TestKey::Mr].name.as_deref(), Some("Recipient"));
}
#[test]
fn test_deserialize_from_map() {
let json = r#"{"items": {
"MS": {"name": "Sender"},
"MR": {"name": "Recipient"}
}}"#;
let container: TestContainer = serde_json::from_str(json).unwrap();
let items = container.items.unwrap();
assert_eq!(items.len(), 2);
assert_eq!(items[&TestKey::Ms].name.as_deref(), Some("Sender"));
}
#[test]
fn test_deserialize_null() {
let json = r#"{"items": null}"#;
let container: TestContainer = serde_json::from_str(json).unwrap();
assert!(container.items.is_none());
}
#[test]
fn test_deserialize_missing_field() {
let json = r#"{}"#;
let container: TestContainer = serde_json::from_str(json).unwrap();
assert!(container.items.is_none());
}
crate::code_enum::key_enum! {
enum GpKey {
Z07 = "Z07",
Z39 = "Z39",
Z40 = "Z40",
}
}
crate::code_enum::map_key! {
GpKey: "partnerrolle", also "datenqualitaet" {
"Z07" => "kundeMsb", None,
"Z39" => "kundeLf", Some("base"),
"Z40" => "kundeLf", Some("informative"),
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(default, rename_all = "camelCase")]
struct Gp {
#[serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_code_string"
)]
partnerrolle: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
datenqualitaet: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
name1: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(default)]
struct GpContainer {
#[serde(
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_keyed_map",
deserialize_with = "deserialize_keyed_map"
)]
gp: Option<BTreeMap<GpKey, Gp>>,
}
fn gp(json: serde_json::Value) -> Result<GpContainer, serde_json::Error> {
serde_json::from_value(serde_json::json!({ "gp": json }))
}
#[test]
fn keyed_map_from_engine_array_resolves_many_to_one() {
let c = gp(serde_json::json!([
{"partnerrolle": "kundeLf", "datenqualitaet": "informative", "name1": "b"},
{"partnerrolle": {"code": "kundeLf", "meaning": null}, "datenqualitaet": "base", "name1": "a"},
{"partnerrolle": "kundeMsb", "name1": "c"}
]))
.unwrap();
let map = c.gp.unwrap();
assert_eq!(map[&GpKey::Z39].name1.as_deref(), Some("a"));
assert_eq!(map[&GpKey::Z40].name1.as_deref(), Some("b"));
assert_eq!(map[&GpKey::Z07].name1.as_deref(), Some("c"));
}
#[test]
fn keyed_map_single_element_serializes_as_object() {
let c = gp(serde_json::json!([{"partnerrolle": "Z07", "name1": "a"}])).unwrap();
assert_eq!(
serde_json::to_value(&c).unwrap(),
serde_json::json!({"gp": {"partnerrolle": "Z07", "name1": "a"}})
);
}
#[test]
fn vec_or_single_serializes_like_the_engine() {
#[derive(Serialize)]
struct S {
#[serde(serialize_with = "serialize_vec_or_single")]
v: Option<Vec<u8>>,
}
let json = |v| serde_json::to_value(S { v }).unwrap();
assert_eq!(json(Some(vec![1])), serde_json::json!({"v": 1}));
assert_eq!(json(Some(vec![1, 2])), serde_json::json!({"v": [1, 2]}));
assert_eq!(json(None), serde_json::json!({"v": null}));
}
#[test]
fn keyed_map_rejects_duplicate_keys() {
let err = gp(serde_json::json!([
{"partnerrolle": "kundeMsb", "name1": "a"},
{"partnerrolle": "Z07", "name1": "b"}
]))
.unwrap_err();
assert!(err.to_string().contains("duplicate map key `Z07`"), "{err}");
}
#[test]
fn keyed_map_rejects_elements_without_key() {
assert!(gp(serde_json::json!([{"name1": "a"}])).is_err());
}
#[test]
fn keyed_map_from_typed_map_fills_qualifier_fields() {
let c = gp(serde_json::json!({"Z40": {"name1": "b"}, "Z07": {"name1": "a"}})).unwrap();
let json = serde_json::to_value(&c).unwrap();
assert_eq!(
json,
serde_json::json!({"gp": [
{"partnerrolle": "kundeMsb", "name1": "a"},
{"partnerrolle": "kundeLf", "datenqualitaet": "informative", "name1": "b"}
]})
);
let again: GpContainer = serde_json::from_value(json.clone()).unwrap();
assert_eq!(serde_json::to_value(&again).unwrap(), json);
}
#[test]
fn keyed_map_rejects_key_disagreeing_with_element() {
let err = gp(serde_json::json!({"Z39": {"partnerrolle": "kundeMsb"}})).unwrap_err();
assert!(err.to_string().contains("disagrees"), "{err}");
}
#[test]
fn keyed_map_single_element_and_null() {
let c = gp(serde_json::json!({"partnerrolle": "kundeMsb"})).unwrap();
assert!(c.gp.unwrap().contains_key(&GpKey::Z07));
assert!(gp(serde_json::Value::Null).unwrap().gp.is_none());
}
#[test]
fn code_string_accepts_string_or_enriched_object() {
#[derive(Deserialize)]
struct S {
#[serde(default, deserialize_with = "deserialize_code_string")]
v: Option<String>,
}
let s: S = serde_json::from_value(serde_json::json!({"v": "Z01"})).unwrap();
assert_eq!(s.v.as_deref(), Some("Z01"));
let s: S =
serde_json::from_value(serde_json::json!({"v": {"code": "Z01", "meaning": "x"}}))
.unwrap();
assert_eq!(s.v.as_deref(), Some("Z01"));
let s: S = serde_json::from_value(serde_json::json!({})).unwrap();
assert!(s.v.is_none());
}
#[test]
fn test_serialize_as_map() {
let mut items = BTreeMap::new();
items.insert(
TestKey::Ms,
TestEntity {
marktrolle: Some("MS".into()),
name: Some("Sender".into()),
},
);
let container = TestContainer { items: Some(items) };
let json = serde_json::to_value(&container).unwrap();
assert!(json["items"].is_object());
assert!(json["items"]["MS"].is_object());
}
}