Json-schema-gen

https://github.com/travisstaloch/json-schema-gen

This lib generates zig code from arbitrary json data which can then parse it.

The schema gen is in python. Maybe I’ll port to zig soon. But here’s how it works. I’m using the build system to

  1. generate a zig file by running the python code
  2. create a module from the generated file
  3. import the module in main.zig
  4. parse the original json file and show its cache path

Hopefully this makes it really quick to generate a zig struct for parsing json!

Motivation

When you need to parse arbitrary json in zig, you usually pass std.json.Value to one of the parse() methods. This is convenient but is generally slower and allocates more memory than passing a concrete type.

Also, std.json.Value can be a little akward at times. Here is how it looks to access data from the github api

with std.json.Value:

const url = parsed.value.object.get("items").?
    .array.items[0].object.get("commits_url").?.string;

with generated schema:

const url = parsed.value.items[0].commits_url;

This library was developed after struggling with code generated by https://aerth.github.io/json-to-zig/. Hopefully std.json gets diagnostics soon cause it can be difficult to tell which fields were causing parse errors.

3 Likes

i made a few changes today:

  • gen:
    • detect field name collisions
    • tweaked the schema format to avoid field name collisions
  • main: use stderr for previous output and print the generated schema to stdout
    so that users can redirect or pipe the generated schema.
  • added some tests
1 Like

i put this up on github pages https://travisstaloch.github.io/

its pretty much a clone of @aerth’s json-to-zig but mostly from scratch and does the conversion in wasm

this required these changes since last time:

  • ported the python script to zig
  • made a wasm build step
  • made a simple web app which calls the wasm module to generate the zig code

the current wasm blob is 71kb release small. i tried adding a minimal panic handler but that didn’t seem to change the size at all. let me know if you have any ideas to make it smaller.

Added support for union and ‘any’ types today. Here are some inputs with before and current output. Missing ‘before’ means unchanged.

  1. ‘any’ arrays
    • a.
      • input: [1, "a"]
      • before: parse error
      • now: std.json.Value
    • b.
      • input: [1, null]
      • now: []const ?i64
  2. union types
    • a.
      • input: [{"a": 1}, {"b": "c"}]
      • before: struct{ a: ?i64 = null, b: ?[]const u8 = null,}
      • now: union(enum) {a: i64, b: []const u8}
    • b.
      • input: [{"a": null}, {"a": 1}]
      • now: []const struct {a: ?i64}
1 Like

I’ve added support for treating input as a schema file instead of a json data file. You can use this on the command line by

  1. generate a schema file
$ zig build
$ zig-out/bin/json-to-zig-schema examples/1.json --dump-schema > /tmp/example-1-schema.json
  1. specify it as input with the new --input-schema flag:
$ zig-out/bin/json-to-zig-schema /tmp/example-1-schema.json --input-schema
pub const Root = []const struct {
    a: struct {
        b: []const struct {
            key: ?[]const u8 = null,
        },
    },
};

There is also a new checkbox which does the same at https://travisstaloch.github.io/