Subject: Re: Abbreviation form for HTTP JSON Header Field Values?

From: Julian Reschke <[email-masked]>
Date: Fri, 8 Jul 2016 19:18:56 +0200

Hi Kazuho,

sorry for not making any progress on this for so long.

I'm still not convinced that your proposal is the right way to do this. 
My main concern being that the code that does the conversion needs to 
understand the JSON data model for the particular header field.

In 
<https://greenbytes.de/tech/webdav/draft-ietf-httpbis-jfv-01.html#example.accept-encoding>, 
I added my own take about how a JSON variant of Accept-Encoding could be 
simplified (accept-encoding was one of your examples):

> 6.4.  Accept-Encoding
>
>    The Accept-Encoding header field value is defined in Section 5.3.4 of
>    [RFC7231] as a list of codings, each of which allowing a weight
>    parameter 'q':
>
>       Accept-Encoding = #( codings [ weight ] )
>       codings         = content-coding / "identity" / "*"
>       weight          = OWS ";" OWS "q=" qvalue
>       qvalue          = ( "0" [ "." 0*3DIGIT ] )
>                       / ( "1" [ "." 0*3("0") ] )
>
>    An example for a complex header field value given in the definition
>    of the header field is:
>
>      gzip;q=1.0, identity; q=0.5, *;q=0
>
>    Due to the defaulting rules for the quality value ([RFC7231], Section
>    5.3.1), this could also be written as:
>
>      gzip, identity; q=0.5, *; q=0
>
>    A JSON representation could be:
>
>      [
>        {
>          "gzip" : {
>          }
>        },
>        {
>          "identity" : {
>            "q": 0.5
>          }
>        },
>        {
>          "*" : {
>            "q": 0
>          }
>        }
>      ]
>
>    ...which would translate to a header field value of:
>
>      {"gzip": {}}, {"identity": {"q": 0.5}}, {"*": {"q": 0}}
>
>    In this example, the part about "gzip" appears unnecessarily verbose,
>    as the value is just an empty object.  A simpler notation would
>    collapse members like these to string literals:
>
>      "gzip", {"identity": {"q": 0.5}}, {"*": {"q": 0}}
>
>    If this is desirable, the header field definition could allow both
>    string literals and objects, and define that a mere string literal
>    would be mapped to a member whose name is given by the string
>    literal, and the value is an empty object.
>
>    For what it's worth, one of the most common cases for 'Accept-
>    Encoding' would become:
>
>      "gzip", "deflate"
>
>    which would be only a small overhead over the original format.

Please let me know what you think.

Best regards, Julian

---

From: Martin Thomson <[email-masked]>
Date: Mon, 11 Jul 2016 14:09:09 +1000

>      "gzip", {"identity": {"q": 0.5}}, {"*": {"q": 0}}

If you accept that each value is a json value, then this is OK, but I
think that I'd prefer simply:
 A-E: { "gzip": {}, "identity": {"q":0.5}, "*": {"q":0} }
The drawback being that you can't include gzip twice in this form.
That's probably a virtue.  See also PHK's comments here.

---

From: Julian Reschke <[email-masked]>
Date: Mon, 11 Jul 2016 08:09:02 +0200

> That's probably a virtue.  See also PHK's comments here.

That's one of the things we need to decide: whether to default to a 
container format (object vs list) or not:

1) If we default to a list, the format will be consistent with multiple 
header field instances with no extra work.

2) If we default to object, you could use the notation above, but loose 
the list behavior.

3) If do not default at all, we'll either always have more delimiters on 
the wire, or need to define heuristics so the recipient knows what to do 
before invoking the JSON parser.

(The draft currently chooses 1)

Best regards, Julian

---

From: Carsten Bormann <[email-masked]>
Date: Mon, 11 Jul 2016 09:14:55 +0200

> simplified (accept-encoding was one of your examples):

accept-encoding = [* coding / coding-with-weight]
coding = text / "identity" / "*" ; simplified
coding-with-weight = { coding => { ? q: 0.0..1.0 } }

$ cddl coding.cddl generate 10

[{"appetize": {}}]
[{"identity": {}}]
[]
[{"*": {"q": 0.29776950937199875}}, "*", "zante"]
["identity"]
[{"westernization": {}}, "deciduate"]
[{"*": {}}, "identity"]
[{"wanderlustful": {}}, {"identity": {"q": 0.006691697664932605}},
"identity"]
["Meissa", {"identity": {}}, "Monroeism", {"pseudoprimitivism": {"q":
0.6130563644945097}}]
["identity", "*"]

(Remove the outer brackets for the actual header field values.)

Grüße, Carsten

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Mon, 11 Jul 2016 09:33:55 +0000

--------
In message <[email-masked]>, Julian Reschke writes

>(The draft currently chooses 1)

I vote strongly for 3 with the "always have more delimiteres on the wire"
option:  Don't straight-jacket the people who have to define new headers.

-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
[email-masked]         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe    
Never attribute to malice what can adequately be explained by incompetence.

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 11 Jul 2016 23:59:44 +0900

Hi Julian,

2016-07-09 2:18 GMT+09:00 Julian Reschke <[email-masked]>:
> I'm still not convinced that your proposal is the right way to do this. My
> main concern being that the code that does the conversion needs to

Thank you for the response.

The accept-encoding's case is very interesting to me.

In case of your example, applying the abbreviation rules would allow
converting `{“q”: N}` to `N` under the premise that `q` is defined as
the default key.

Taking your example, the rules would allow converting

    {"gzip":{}}, {"identity":{"q":0.5}}, {"*":{"q":0}}

to

    {"gzip":{}}, {"identity":0.5}, {"*":0}

The impact of the rules is not so significant here since the name of
the default key `q` is short.

In other words, the impact of abbreviation is proportional to the
length of the default key plus the depth of the JSON structure. So we
might be able to live without having abbreviation rules if we can keep
the names of the default keys short.

P.S. I agree with Martin and PHK that it would be better to present
the entire header using a single hash whenever possible.

-- 
Kazuho Oku