git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 0/2] routines to generate JSON data

From
JHJeff Hostetler <git@jeffhostetler.com>
Date
Mar 19, 2018, 10:19 UTC
Message-ID
<e2a9d2a9-f2e2-f285-52c3-be11668da543@jeffhostetler.com>
In-Reply-To
<20180316211837.GB12333@sigill.intra.peff.net>
On 3/16/2018 5:18 PM, Jeff King wrote:
> On Fri, Mar 16, 2018 at 07:40:55PM +0000, git@jeffhostetler.com wrote:
> 
[...]
Show 7 quoted lines
> I really like the idea of being able to send our machine-readable output
> in some "standard" syntax for which people may already have parsers. But
> one big hangup with JSON is that it assumes all strings are UTF-8. That
> may be OK for telemetry data, but it would probably lead to problems for
> something like status porcelain, since Git's view of paths is just a
> string of bytes (not to mention possible uses elsewhere like author
> names, subject lines, etc).
[...]
I'll come back to the UTF-8/YAML questions in a separate response.
Show 32 quoted lines
>> Documentation for the new API is given in json-writer.h at the bottom of
>> the first patch.
> 
> The API generally looks pleasant, but the nesting surprised me.  E.g.,
> I'd have expected:
> 
>    jw_array_begin(out);
>    jw_array_begin(out);
>    jw_array_append_int(out, 42);
>    jw_array_end(out);
>    jw_array_end(out);
> 
> to result in an array containing an array containing an integer. But
> array_begin() actually resets the strbuf, so you can't build up nested
> items like this internally. Ditto for objects within objects. You have
> to use two separate strbufs and copy the data an extra time.
> 
> To make the above work, I think you'd have to store a little more state.
> E.g., the "array_append" functions check "out->len" to see if they need
> to add a separating comma. That wouldn't work if we might be part of a
> nested array. So I think you'd need a context struct like:
> 
>    struct json_writer {
>      int first_item;
>      struct strbuf out;
>    };
>    #define JSON_WRITER_INIT { 1, STRBUF_INIT }
> 
> to store the state and the output. As a bonus, you could also use it to
> store some other sanity checks (e.g., keep a "depth" counter and BUG()
> when somebody tries to access the finished strbuf with a hanging-open
> object or array).

Yeah, I thought about that, but I think it gets more complex than that. I'd need a stack of "first_item" values. Or maybe the _begin() needs to increment a depth and set first_item and the _end() needs to always unset first_item. I'll look at this gain.

The thing I liked about the bottom-up construction is that it is easier to collect multiple sets in parallel and combine them during the final roll-up. With the in-line nesting, you're tempted to try to construct the resulting JSON in a single series and that may not fit what the code is trying to do. For example, if I wanted to collect an array of error messages as they are generated and an array of argv arrays and any alias expansions, then put together a final JSON string containing them and the final exit code, I'd need to build it in parts. I can build these parts in pieces of JSON and combine them at the end -- or build up other similar data structures (string arrays, lists, or whatever) and then have a JSON conversion step. But we can make it work both ways, I just wanted to keep it simpler.

Show 21 quoted lines
>> I wasn't sure how to unit test the API from a shell script, so I added a
>> helper command that does most of the work in the second patch.
> 
> In general I'd really prefer to keep the shell script as the driver for
> the tests, and have t/helper programs just be conduits. E.g., something
> like:
> 
>    cat >expect <<-\EOF &&
>    {"key": "value", "foo": 42}
>    EOF
>    test-json-writer >actual \
>      object_begin \
>      object_append_string key value \
>      object_append_int foo 42 \
>      object_end &&
>    test_cmp expect actual
> 
> It's a bit tedious (though fairly mechanical) to expose the API in this
> way, but it makes it much easier to debug, modify, or add tests later
> on (for example, I had to modify the C program to find out that my
> append example above wouldn't work).

Yeah, I wasn't sure if such a simple api required exposing all that machinery to the shell or not. And the api is fairly self-contained and not depending on a lot of disk/repo setup or anything, so my tests would be essentially static WRT everything else.

With my t0019 script you should have been able use -x -v to see what was failing.

> 
> -Peff
> 

thanks for the quick review Jeff

Previous: Jeff HostetlerNext: Jeff King
Message 9 of 11 in “routines to generate JSON data”
  1. 0/2 routines to generate JSON datagit@jeffhostetler.com, Mar 16, 2018
  2. 2/2 json-writer: unit testgit@jeffhostetler.com, Mar 16, 2018
  3. 1/2 json_writer: new routines to create data in JSON formatgit@jeffhostetler.com, Mar 16, 2018
  4. Jeff KingMar 16, 2018
  5. Ævar Arnfjörð BjarmasonMar 16, 2018
  6. Jeff KingMar 20, 2018
  7. Jacob KellerMar 17, 2018
  8. Jeff HostetlerMar 19, 2018
  9. Jeff HostetlerMar 19, 2018
  10. Jeff KingMar 20, 2018
  11. Jeff HostetlerMar 20, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.