Parse JSON case-sensitively

573 views
Skip to first unread message

Aaron

unread,
Sep 23, 2021, 2:23:47 PM9/23/21
to golang-nuts
Hi all

Anyone know of a JSON package (or other solution) which is case-sensitive when decoding - and is also mature, simple, and parses into a struct?
I've looked at a few open-source packages, but the ones I've found seem to be focused on performance, or allowing you to access the JSON in different ways, or require code-generation, or something.
Really I just want a drop-in replacement for encoding/json that is case sensitive.
Wondering about forking the package myself... but obviously don't want to do that if there's an existing solution.

Thanks

Tyler Compton

unread,
Sep 23, 2021, 4:53:23 PM9/23/21
to Aaron, golang-nuts
Hi Aaron,

I'm not aware of a JSON library that fits your needs, but there might be an XY Problem going on here. What problem are you having trouble solving with encoding/json that lead you to this solution?

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/a3d25416-2490-4719-a38c-9f6a43008184n%40googlegroups.com.

Brian Candler

unread,
Sep 24, 2021, 3:25:31 AM9/24/21
to golang-nuts
Wow, I didn't realise until now that Unmarshal does case-insensitive matching:
The documentation says it prefers exact match over case-insensitive, but example 3 contradicts that.  It seems to be last-match that wins.

Regardless of any X-Y issue, this seems broken to me. If I define a particular API, I want to enforce that it's being used in the way that I defined.  Accepting arbitrary variations just stores up problems for the future.
Case-insensitivity can open up its own issues: e.g. https://www.theregister.com/2021/09/22/macos_rce_flaw/

As a workaround, you could use a YAML decoder to decode JSON.  JSON is a subset of YAML.  If you don't want to accept YAML as well, then you might have to decode it twice: once to confirm that it's structurally JSON, and again to extract the data.

Brian Candler

unread,
Sep 24, 2021, 4:03:32 AM9/24/21
to golang-nuts
On Friday, 24 September 2021 at 08:25:31 UTC+1 Brian Candler wrote:
The documentation says it prefers exact match over case-insensitive, but example 3 contradicts that.  It seems to be last-match that wins.


I realise now what they mean now: if there are multiple struct tags defined which differ only in case, then the one which matches the incoming field exactly is preferred.

Dan Kortschak

unread,
Sep 24, 2021, 4:37:04 AM9/24/21
to golan...@googlegroups.com
This still seems to conflict with the JSON-RPC spec:
https://jsonrpc.org/historical/json-rpc-1-1-alt.html#service-procedure-and-parameter-names

And it doesn't even appear to use the exact match in preference
depending on the order of the JSON keys
https://play.golang.org/p/SQyE3R-GGNn


Ian Davis

unread,
Sep 24, 2021, 4:56:12 AM9/24/21
to golan...@googlegroups.com
I think this is working as expected. For each key in the input json the unmarshaller looks for a matching struct field to hold the data preferring an exact match and falling back to case insensitivity. If there are multiple json keys that map to a single field then they will overwrite any previously matched keys.

This modification to your example illustrates it better: https://play.golang.org/p/Ipp67LMG1Kt

Ian

Dan Kortschak

unread,
Sep 24, 2021, 5:01:21 AM9/24/21
to golan...@googlegroups.com
I agree that it can be made to work; I constructed the example that way
it is to demonstrate the failure. Needing to guard case that way is
pretty surprising and certainly makes it impossible to implement a
spec-compliant JSON-RPC on top of encoding/json.


Ian Davis

unread,
Sep 24, 2021, 5:23:41 AM9/24/21
to golan...@googlegroups.com
I was responding to your statement that it doesn't appear to use exact match in preference. It does, as the example I gave demonstrated. It's not about guarding case.

Ian

Dan Kortschak

unread,
Sep 24, 2021, 5:37:08 AM9/24/21
to golan...@googlegroups.com
On Fri, 2021-09-24 at 10:22 +0100, Ian Davis wrote:
> I was responding to your statement that it doesn't appear to use
> exact match in preference. It does, as the example I gave
> demonstrated. It's not about guarding case.


I'll clarify.

In the example I posted https://play.golang.org/p/SQyE3R-GGNn, their is
clearly an assignment to a non-exact match despite an exact match being
present. In the example you posted, you show that this can be prevented
by providing another exactly matching field (this may not be what you
intended to show, but it does show that). This feature can be used to
prevent an incorrect assignment of the other-cased key-value like here
https://play.golang.org/p/Fk8CHy_v7Gg. This may be necessary if you
have incoming JSON that expects case to be considered and you don't
want/need of the cases in your deserialisation (this is what I mean by
case guarding).


Ian Davis

unread,
Sep 24, 2021, 6:21:50 AM9/24/21
to golan...@googlegroups.com
This is not a correct interpretation. In your example the unmarshaller reads the incoming json. The first key encountered is "Name" and it matches exactly with the field named and tagged as Name so that field is assigned the value. The next json key encountered is "name" which does not match any field exactly so the unmarshaller falls back to a case insensitive match and assigns the value to the Name field, overwriting the previous value.

Both the exact match and the case insensitive match are performed in this example. There is no "incorrect" assignment as you describe.

Ian

Dan Kortschak

unread,
Sep 24, 2021, 6:31:43 AM9/24/21
to golan...@googlegroups.com
On Fri, 2021-09-24 at 11:21 +0100, Ian Davis wrote:
> This is not a correct interpretation. In your example the
> unmarshaller reads the incoming json. The first key encountered is
> "Name" and it matches exactly with the field named and tagged as Name
> so that field is assigned the value. The next json key encountered is
> "name" which does not match any field exactly so the unmarshaller
> falls back to a case insensitive match and assigns the value to the
> Name field, overwriting the previous value.
>
> Both the exact match and the case insensitive match are performed in
> this example. There is no "incorrect" assignment as you describe.

I guess this depends on what a person considers to be correct. If you
are writing code to comply with the JSON-RPC spec (
https://www.jsonrpc.org/specification), then I'd say that this is an
incorrect assignment.



Dan Kortschak

unread,
Sep 24, 2021, 6:41:12 AM9/24/21
to golan...@googlegroups.com
On Fri, 2021-09-24 at 11:21 +0100, Ian Davis wrote:
> This is not a correct interpretation. In your example the
> unmarshaller reads the incoming json. The first key encountered is
> "Name" and it matches exactly with the field named and tagged as Name
> so that field is assigned the value. The next json key encountered is
> "name" which does not match any field exactly so the unmarshaller
> falls back to a case insensitive match and assigns the value to the
> Name field, overwriting the previous value.

Yeah, you're right. I was not thinking about the mechanism correctly.
My point stands wrt the impact on utility though.


Alex Howarth

unread,
Sep 24, 2021, 3:33:43 PM9/24/21
to Aaron, golang-nuts
Could you perhaps achieve this with reflect and UnmarshalJSON() ?

The following basic example compares against the case-sensitive json tag:

--
Reply all
Reply to author
Forward
0 new messages