Customize sum type JSON encoding and decoding #23609
Replies: 5 comments 1 reply
-
Just an idea https://play.vlang.io/p/8ce5492e7b : import json
struct Item {
id string @[omitempty]
name string @[omitempty]
action Action
}
enum Action {
add
update
delete_all
}
fn main() {
println(json.encode_pretty([
Item{ id:'1', name:'item', action:.add },
Item{ id:'1', name:'item1-updated', action:.update },
Item{ action:.delete_all }
]))
}
|
Beta Was this translation helpful? Give feedback.
-
The |
Beta Was this translation helpful? Give feedback.
-
To conform with your last json you could do this: import json
struct Elem {
typ Type @[json:"type"]
value Value @[omitempty]
}
struct Value {
item Item
}
struct Item {
id string
name string
}
enum Type {
add_item
update_item
delete_all_items
}
println(json.encode_pretty([
Elem{ typ:.add_item, value:Value{ item:Item{id:'1', name:'item1'}} },
Elem{ typ:.update_item, value:Value{ item:Item{id:'1', name:'item1-updated'}} },
Elem{ typ:.delete_all_items }
]))
|
Beta Was this translation helpful? Give feedback.
-
Thank you for your replies @jorgeluismireles. I see you suggest using an enum instead of a sum type. Let me expand on my example a bit to better illustrate why I prefer to use a sum type:
struct Item {
id string
name string
}
struct AddItem {
item Item
}
struct UpdateItem {
item Item
}
struct DeleteItem {
id string
}
struct DeleteAllItems {}
type Action = AddItem | UpdateItem | DeleteItem | DeleteAllItems
fn transform_action(action Action) string {
return match action {
AddItem {
'add_item ${action.item.id} ${action.item.name}'
}
UpdateItem {
'update_item ${action.item.id} ${action.item.name}'
}
DeleteItem {
// type error: `DeleteItem` has no field named `item`
'delete_item ${action.item.id}'
}
DeleteAllItems {
'delete_all_items'
}
}
} You can see that matching on the If I were to write this example using the enum pattern, I imagine it would look something like this: module main2
enum ActionType {
add
update
delete
delete_all
}
struct Action {
type ActionType
item Item @[omitempty]
id string @[omitempty]
}
struct Item {
id string
name string
}
fn transform_action(action Action) string {
return match action.type {
.add {
'add_item ${action.item.id} ${action.item.name}'
}
.update {
'update_item ${action.item.id} ${action.item.name}'
}
.delete {
// no type error!
'delete_item ${action.item.id}'
}
.delete_all {
'delete_all_items'
}
}
} And in this case since Thus my choice of using sum types as runtime representation, and my original question of being able to customize its JSON encoding/decoding. |
Beta Was this translation helpful? Give feedback.
-
This is still 2025, so I don't think it qualifies as a necrobump. A real use case is jsonrpc, which is a defined standard and we don't have the luxury of changing the JSON format to suit V. jsonrpc results are defined (more or less, cut down) like this: {
"id": 5,
"jsonrpc": "2.0",
"result": {
<string key with type name>: <serialized thing>
}
It's tantalizingly close to define an API like this: struct First {
name string
}
struct Second {
quantity int
}
type Result = First | Second
struct Response {
id int
jsonrpc string
result Result
} because jsonrpc is going to give you:
But clearly, this does not work.
1 may be a red herring; it's useless information unless it's used by the decoder to figure out the sum type. The API user usually doesn't care about it for jsonrpc, but for other APIs it might be important. I think a solution would involve being able to put json attributes on structs, like this: @[json: "first"]
struct First {
name string
} If the json encoder found such an attribute, it would serialize the struct as: { "first": { "name": "" } } instead of {"name": ""} Similarly, the decoder would expect the first JSON instead of the second if it did find such an attribute on the type. This would solve both issues in one stroke with minimal language change. Is there another solution that works with the assumption that the JSON representation provided by the server is not under our control? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Is there a way to customize the JSON encoding/decoding of sum types?
Here is a simplified example of what I'm working with:
This outputs:
What if I'd like the "tag" property to be
"type"
instead of"_type"
, and the tag values to be snake case (ex:"add_item"
instead of"AddItem"
)? To my knowledge that is not possible at the moment in V.In Rust, using Serde's different enum representations, I can specify the attribute arguments
tag
andrename_all
as follows:Which would produce the desired JSON format:
Another use-case would be wanting to use the "adjacently tagged" format instead of the "internally tagged" format that is currently produced by V.
Again using Rust as an example, I can add the Serde attribute argument
content
:Which would produce:
One of the reason I'm asking is it would make working with existing APIs that use these formats to represent sum types (or discriminated unions) easier?
Beta Was this translation helpful? Give feedback.
All reactions