json icon indicating copy to clipboard operation
json copied to clipboard

I'd like a nicer api to create objects without null properties

Open CrowdHailer opened this issue 5 months ago • 3 comments

I often write the following

json.object([
  #("foo", json.nullable(x, json.int)
])

Which will create {foo: null} when x is None. What I often want is {} when x is null several API's in the wild crash if an optional field is instead sent with a value of null.

A possible implementation was suggested in https://github.com/gleam-lang/json/pull/31 though it's not clear this is the best PR.

CrowdHailer avatar Jul 11 '25 14:07 CrowdHailer

I've used something roughly like this before, but I'm not totally sure this is right to add just yet. Could you share some examples of the code where you've needed this function?

If a function is added I think it would be better to not use null as the value to indicate when it should omit a property is it means you can't retain any null values, and it seems a bit unusual for Gleam code to use some special value rather than wrapping it in a custom type or a new function.

lpil avatar Jul 15 '25 14:07 lpil

I use it in generated code from Open API Specs https://github.com/midas-framework/midas_sdk/blob/main/sdks/at_protocol/src/at_protocol/utils.gleam#L63-L69

I've used it at work internally in a few places. but none of that is on github. The other option I think is to take a list of options

json.sparse([
  #("foo", Some(json.int(5))),
  #("bar", Some(json.null())),
  #("baz", None)
])
{"foo": 5, "bar": null}

CrowdHailer avatar Jul 15 '25 22:07 CrowdHailer

I'd agree that using a JSON null as the switch is weird. In which case in my option the only remaining sensible option = is to use a Gleam Custom Type for the value. I think Option fit's fine here.

The implementation would be

fn sparse(properties) {
  list.filter_map(properties, fn(property) {
    let #(key, value) = property
    case value {
      Some(value) -> Ok(#(key, value))
      None -> Error(Nil)
    }
  })
  |> json.object
}

This allows you so set a null field with json.sparse([#("foo", Some(json.null()))])

It came up again in my project on MCP servers.

CrowdHailer avatar Aug 07 '25 09:08 CrowdHailer