I need to deserialize ( and later on serialize ) a piece of data that has this type of a structure :
{
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"Record"
]
},
"arg": {
"type": "RowCons",
"val": {
"label": "953e3dd6-826e-1985-cb99-fd4ed4da754e",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "nullam-senectus-port - Text",
"isBindable": true,
"defaultValue": [
{
"id": "4a05486f-f24d-45f8-ae13-ab05f824d74d",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Nullam senectus porttitor in eget. Eget rutrum leo interdum."
},
"children": [],
"text": true
}
],
"isUnlinked": false,
"isDefault": false
}
},
"tail": {
"type": "RowCons",
"val": {
"label": "94f603df-d585-b45a-4252-9ec77cf5b13c",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "best-services - Text",
"isBindable": true,
"defaultValue": [
{
"id": "6265ca45-3f69-4844-97e2-c05bbfb9fee5",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Best Services"
},
"children": [],
"text": true
}
]
}
},
"tail": {
"type": "RowEmpty"
}
}
}
}
}
}
}
I do not know what this data exactly is, but I know this is trying to represent a function/element that takes in values and defaults for those values as parameters/properties.
I want to deserialize it using serde and consequently serialize it too. I have so far been able to write something that sort of works but not really :
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type", content = "val")]
pub enum WebflowPropDataType {
TypeApplication {
con: Box<WebflowPropDataType>, // Normally Passes the Type Constructor
arg: Box<WebflowPropDataType>, // Normally Passes the Row Constructor
},
TypeConstructor(Vec<String>), // Stores Value of TypeConstructor
RowCons {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
row_con_type: Box<WebflowPropDataType>, // Stores the type of the Row
tail: Box<WebflowPropDataType>,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct WebflowRowConDataType {
#[serde(rename = "type")]
val_type: String, // TypeApplication
val: Box<WebflowPropDataType>,
}
This works for a structure like this :
{
"type": "TypeApplication",
"val":{
"con": {
"type": "TypeConstructor",
"val": []
},
"arg": {
"type": "RowEmpty"
}
}
}
but would fail if I try to work with initial example. I know this may be due to the lack of a proper arg
type or maybe even the TypeApplication
Enum hand is malformed.
I do see that a adjancent typing solution would be enough for most of the times but there are cases as seen in the example structure that there is a third value called system
and I am unable to determine what type of approach would help me achieve this type of outcome.
How should I approach this problem in order to generate this type of code.
I am not asking anyone to write me a solution but to give me suggestion as to what my approach should be? Whether you'd know what type of data this is/how to generated this , or even if there are some other library I should look into to manipulate this type of data or maybe look at other approaches.
PS : - My end goal is to be able to generate / serialize this type of JSON code from already contained knowledge of properties and default values of a function/object.
CodePudding user response:
Here are my recommendations:
Use just
#[serde(tag = "type")]
instead of#[serde(tag = "type", content = "val")]
. You will have to handleval
manually (extracting the current enum members into separate structs), but this allows you to also handleTypeApplication.system
andElement.meta
.This also has the small benefit of reducing the amount of
Box
es involved.Consider whether all of the different cases in
WebflowPropDataType
can actually occur everywhere it's used. If not (maybeElement
can only happen underTypeApplication.val.arg
), then you may want to split the enum into multiple so that this is reflected in the type system.
Example for #1:
use serde::{Serialize, Deserialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationVal {
con: WebflowPropDataType, // Normally Passes the Type Constructor
arg: WebflowPropDataType, // Normally Passes the Row Constructor
}
// #[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationSystem {
label: String,
#[serde(rename = "isBindable")]
is_bindable: bool,
// TODO: defaultValue
#[serde(rename = "isUnlinked")]
#[serde(skip_serializing_if = "Option::is_none")]
is_unlinked: Option<bool>,
#[serde(rename = "isDefault")]
#[serde(skip_serializing_if = "Option::is_none")]
is_default: Option<bool>,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct RowConsVal {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
typ: WebflowPropDataType, // Stores the type of the Row
tail: WebflowPropDataType,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ElementMeta {
#[serde(rename = "multiLine")]
multi_line: bool,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum WebflowPropDataType {
TypeApplication {
val: Box<TypeApplicationVal>,
#[serde(skip_serializing_if = "Option::is_none")]
system: Option<TypeApplicationSystem>,
},
TypeConstructor {
val: Vec<String> // Stores Value of TypeConstructor
},
RowCons {
val: Box<RowConsVal>,
},
Element {
meta: ElementMeta,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}