#gserde - JSON serializer and deserializer codegen for constructors

1 messages · Page 1 of 1 (latest)

south terrace
#

https://hexdocs.pm/gserde/index.html

Don't want to write all the serializers and parsers for your models? Generate them.

It's very crude ATM, but hey--works-ish!

Demo from my actual project:

// src/model.gleam

// types ending in JSON get generated modules
pub type ModelJSON {
  Model(
    gimbal_url: Option(String),
    connected: Bool,
    history: List(String),
    is_streaming: Bool,
    manual_control_degrees: #(Int, Int),
  )
}

Then, gleam run -m gserde. Then blamo, you get to_json(t), to_string(t), and from_string(json_string).

// src/model_json.gleam
import gleam/json
import gleam/dynamic
import model

pub fn to_json(t: model.ModelJSON) {
  json.object([
    #("gimbal_url", json.nullable(t.gimbal_url, json.string)),
    #("connected", json.bool(t.connected)),
    #("history", json.array(t.history, json.string)),
    #("is_streaming", json.bool(t.is_streaming)),
    #(
      "manual_control_degrees",
      json.preprocessed_array([
        json.int(t.manual_control_degrees.0),
        json.int(t.manual_control_degrees.1),
      ]),
    ),
  ])
}

pub fn to_string(t: model.ModelJSON) {
  json.to_string(to_json(t))
}

pub fn from_json(json_str: String) {
  json.decode(
    json_str,
    dynamic.decode5(
      model.Model,
      dynamic.field("gimbal_url", dynamic.optional(dynamic.string)),
      dynamic.field("connected", dynamic.bool),
      dynamic.field("history", dynamic.list(dynamic.string)),
      dynamic.field("is_streaming", dynamic.bool),
      dynamic.field(
        "manual_control_degrees",
        dynamic.tuple2(dynamic.int, dynamic.int),
      ),
    ),
  )
}

again, not a super polished impl, and has macro shortcomings. something for another weekend (or your prs welcome) 🙂

#

gserde - JSON serializer and deserializer codegen for constructors

wild torrent
#

does this work for fields that arent primitives?

elfin plover
#

I probably wouldn't call it gserde, they don't really do the same thing, maybe json_codegen or something like that, but I'm pumped gleam

south terrace
#

does this work for fields that arent primitives?
no, not yet. even Result doesn't work. enums get wonky because you need to specify a tagging strategy... and i didn't want to build all that on 1st pass

I probably wouldn't call it gserde,
eh, fair. but also... grice's razor a lil bit

wild torrent
#

already published who cares

#

nice package 💕

lapis bobcat
#

nice, was planning json codec as guinnea pig for my codegen 😉 but still having priorities programmig on other things.

#

more than happy to accept more PRs to make things posible, and I'll do that 0.1.0 tomorrow, then.

south terrace
#

same same. maybe that will be a better winner long term anyway. uncertain 🙂

south terrace
#

does this work for fields that arent primitives?

basic support now added. but all referenced types must also have corresponding public _json.gleam modules

// bar.gleam
import internal/foo

pub type BarJson {
  Bar(foo: foo.FooJson)
}

// foo.gleam
import gleam/option.{type Option, Some}

pub type FooJson {
  Foo(
    a_bool: Bool,
    b_int: Int,
    c_float: Float,
    d_two_tuple: #(Int, String),
    e_option_int: Option(Int),
    f_string_list: List(String),
  )
}
#

now you can ser[de] BarJson, which references a FooJson!

#

would be nice to get these _Json_s out of the type names

#

maybe another day

lucid tundra
#

I've been playing with it a bit, I really like the idea !

I wonder if it'd be worth being more compatible with wisp as it expects a StringBuilder in wisp.json_response(json, 200) but the _json returns a string (not so important but would play quite nicely).

Same for the from_json which expects a string, but wisp.require_json(req) returns a Dynamic (or am I missing something easy).

Maybe it could have a --wisp parameter to output a _json file that plays nicely with wisp out of the box? Or is there one option more sound than the other?

south terrace
#

I expose the decoder. I could also expose the encoder. That’s probably 95% of the effort, which really isn’t even that much effort. I haven’t used wisp before—i wonder why it wants a builder