Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider erroring on JSON duplicate keys instead of "last value wins" #34

Closed
zamicol opened this issue Jul 10, 2023 · 3 comments
Closed

Comments

@zamicol
Copy link

zamicol commented Jul 10, 2023

For an implementation with this rule applied, see https://github.com/Cyphrme/Coze/blob/76f019b67712c0dc16cdde9895cff5448342d63f/orderedmap.go#L31

Duplicate fields are a problem in JSON that wasn't addressed by the original spec.

After JSON was widely adopted, Douglas Crockford, JSON's inventor, tried to fix this but it was decided it
was too late
.

Although Douglas Crockford couldn't change the spec forcing all implementations
to error on duplicate, his Java JSON implementation errors on duplicate names.
Others use last-value-wins, support duplicate keys, or other non-standard
behavior. The JSON
RFC
states that
implementations should not allow duplicate keys, notes the varying behavior
of existing implementations, and states that when names are not unique, "the
behavior of software that receives such an object is unpredictable." Also note
that Javascript objects (ES6) and Go structs already require unique names.

Duplicate fields are a security issue, a source of bugs, and a surprising
behavior to users. See the article, "An Exploration of JSON Interoperability
Vulnerabilities
"

Disallowing duplicates conforms to the small I-JSON RFC. The author of I-JSON,
Tim Bray, is also the author of current JSON specification (RFC 8259). See also
json5/json5-spec#38.

@iancoleman
Copy link
Owner

This is a really informative comment, thanks for raising it, much appreciated.

The behavior of orderedmap will follow the standard map functionality in golang, so I can't see this change being implemented unless go changes the behavior of the base map type.

Further reference https://go.dev/ref/spec#Composite_literals

For map literals, all elements must have a key. It is an error to specify multiple elements with the same field name or constant key value. For non-constant map keys, see the section on evaluation order.

An area that I feel is good to explore is using const for keys, which might be desired to throw a compile-time error. In this lib, const keys don't throw that error. eg:

func TestDuplicateKeys(t *testing.T) {
    // can use duplicate keys if not const, works same as map[key] = val
    canDuplicate := New()
    canDuplicate.Set("notConstKey", "initial")
    canDuplicate.Set("notConstKey", "overriden")
    val, _ := canDuplicate.Get("notConstKey")
    // expect duplicate key to be overridden
    if val.(string) == "initial" {
        t.Error("Duplicate key was not overridden")
    }
    // can not use duplicate keys if const - maybe not the best behavior by the lib here?
    cannotDuplicate := New()
    const constKey string = "constKey"
    cannotDuplicate.Set(constKey, "initial")
    // possible compile error on the next line, but it doesn't
    cannotDuplicate.Set(constKey, "duplicated")
}

I'm open to further thoughts on this issue.

@zamicol zamicol changed the title Consider erroring on duplicate keys instead of "last value wins" Consider erroring on JSON duplicate keys instead of "last value wins" Jul 13, 2023
@zamicol
Copy link
Author

zamicol commented Jul 13, 2023

Sorry I should have mentioned this is only for JSON, so the check would be added to UnmarshalJSON. It is not changing the behavior of OrderedMap itself which would still work like Go's map.

See the pull request: #35

@iancoleman
Copy link
Owner

The PR is good and I'd like to merge that if possible, let's keep track of the discussion there. I'll close this and have further discussion in #35.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants