Skip to content

Instantly share code, notes, and snippets.

@packrat386
Created February 19, 2020 23:01
Show Gist options
  • Save packrat386/b1afed8aa6dc1f66ef0db43cb999aa41 to your computer and use it in GitHub Desktop.
Save packrat386/b1afed8aa6dc1f66ef0db43cb999aa41 to your computer and use it in GitHub Desktop.
A map that errors out if you attempt to marshal JSON with duplicate keys
package wat
import (
"bytes"
"encoding/json"
"fmt"
)
type noDupMap map[string]string
func (n *noDupMap) UnmarshalJSON(b []byte) error {
*n = map[string]string{}
fmt.Println(string(b))
dec := json.NewDecoder(bytes.NewBuffer(b))
tok, err := dec.Token()
if err != nil {
return err
}
d, ok := tok.(json.Delim)
if !ok {
return fmt.Errorf("didn't get a delimiter")
}
if d.String() != "{" {
return fmt.Errorf("expected '{', got: %s", d.String())
}
for {
tok, err := dec.Token()
if err != nil {
return err
}
if d, ok := tok.(json.Delim); ok {
if d.String() != "}" {
return fmt.Errorf("expected '}', got: %s", d.String())
}
break
}
k, ok := tok.(string)
if !ok {
fmt.Errorf("expected string, got: '%s'", tok)
}
if _, ok := (*n)[k]; ok {
return fmt.Errorf("duplicate key: %s", k)
}
var v string
err = dec.Decode(&v)
if err != nil {
return fmt.Errorf("error reading value: %w", err)
}
(*n)[k] = v
}
if dec.More() {
return fmt.Errorf("too much input")
}
return nil
}
package wat
import (
"encoding/json"
"fmt"
"testing"
)
func TestDedup(t *testing.T) {
input := `{"foo":"a", "bar":"b", "foo":"c"}`
var n noDupMap
err := json.Unmarshal([]byte(input), &n)
fmt.Println(n)
fmt.Println(err)
if err == nil {
t.Fatal("should have errored")
}
}
func TestWorks(t *testing.T) {
input := `{"foo":"a", "bar":"b"}`
var n noDupMap
err := json.Unmarshal([]byte(input), &n)
fmt.Println(n)
fmt.Println(err)
if err != nil {
t.Fatalf("done broke: %s", err.Error())
}
}
func TestBad(t *testing.T) {
input := `{"foo":"a", "a":1}`
var n noDupMap
err := json.Unmarshal([]byte(input), &n)
fmt.Println(n)
fmt.Println(err)
if err == nil {
t.Fatal("should have errored")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment