Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wit/bindgen: support JSON encoding of WIT types #239

Open
2 of 11 tasks
lxfontes opened this issue Nov 7, 2024 · 3 comments
Open
2 of 11 tasks

wit/bindgen: support JSON encoding of WIT types #239

lxfontes opened this issue Nov 7, 2024 · 3 comments
Assignees

Comments

@lxfontes
Copy link
Member

lxfontes commented Nov 7, 2024

This started in #225

Goal: JSON Serialize / Deserialize WIT Records based on WIT field names.

Progress

Approach

Centralize JSON serialization in the cm package, keeping codegen changes to a minimum.

Given a complex record type:

  record response {
      headers: list<tuple<string, list<string>>>,
      http-code: u16,
      body: response-body
  }
  variant response-body {
    ok(list<list-element>),
    err(function-error),
    platform-err(platform-error)
  }
  record list-element {
      optional-int: option<u64>,
      optional-bool: option<bool>,
  }
  record function-error {
      error: string
  }
  record platform-error {
    code: string,
    message: string
  }

and filling it up:

	hdrVals := cm.ToList([]string{"value1", "value2"})
	hdrTuple := cm.Tuple[string, cm.List[string]]{
		F0: "header-name",
		F1: hdrVals,
	}
	headers := cm.ToList([]cm.Tuple[string, cm.List[string]]{hdrTuple})
	v := somefunctioninterface.Response{
		Headers:  headers,
		HTTPCode: 200,
		Body:     somefunctioninterface.ResponseBodyErr(somefunctioninterface.FunctionError{Error: "failed"}),
	}

We should serialize it to JSON as:

{"headers":[["header-name",["value1","value2"]]],"http-code":200,"body":{"err":{"error":"failed"}}}

For comparison, this is what is produced today:

{"Headers":{},"Body":{"RequiredParam":"required","OptionalParam":{}}}

Type encoding

Whenever possible, reuse standard mappings. string -> string, u32 -> uint32, etc.

Tuple handling

Tuples are encoded as json arrays with explicit nulls.

Tuple[string,int] -> [ "some string", 42 ]
Tuple3[string, *string, int] -> [ "some string", null, 42 ]

Variant handling

Variants are encoded as json dictionaries, so they can carry the variant data.

 record somerecord {
   myvariant: somevar,
 }
  variant somevar {
    ok,
    err(string),
  }

// JSON
{ "myvariant": { "ok": true }}
{ "myvariant": { "error": "error value" }}

Option handling

Options rely on explicit null for the "None" case.

{ "myoptional": null } // cm.None
{ "myoptional": "this" } // cm.Option[string]

Questions

Zero Value Variants

Today they end up with tag = 0, this impacts de-serialization of variants. We need the ability to distinct between:

  • var v Variant
  • v := NewVariantWithZeroTagValue()
  • v := Some(NewVariantWithZeroTagValue)

atm only the Some() path de-serializes correctly ( pointers ).

@lxfontes lxfontes self-assigned this Nov 7, 2024
@ydnar ydnar changed the title marshal & unmarshal for record types wit/bindgen: JSON serialization support for record types Nov 7, 2024
@ydnar
Copy link
Collaborator

ydnar commented Nov 10, 2024

Can we move the design and task list for this to an issue?

Then we can have discrete PRs that implement JSON support for different types?

@ydnar ydnar changed the title wit/bindgen: JSON serialization support for record types wit/bindgen: support JSON encoding of WIT types Dec 28, 2024
@ydnar
Copy link
Collaborator

ydnar commented Jan 5, 2025

Proposal for option, result, and variant JSON:

Option

The current proposal specifies null for option<T> when it is the none case. If option values are nested, this creates an ambiguity as to which option is some versus none, e.g. option<option<T>>.

An alternate proposal would be to specify {"none": ...} instead of null to disambiguate nested options.

For an option<string> with the some case:

{"some":"foo"}

For an option<string> with the none case:

{"none":null}

For an option<option<string>> where the second option is the none case:

{"some":{"none":null}}

Result

result<T, E> values would serialize similarly to option as a simplified variant.

For result<string> in the OK case (no error type):

{"ok": "some OK value"}

For result<string> in the error case:

{"error": null}

@ydnar
Copy link
Collaborator

ydnar commented Jan 5, 2025

For resource, stream, future, and error-context types, I think JSON serialization should be an error, similar to how chan and func types cannot be marshaled into JSON in Go today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants