Skip to content

Commit

Permalink
Added the functionality to deserialize orderedmaps
Browse files Browse the repository at this point in the history
        into CustomTypes like `JSDict`.
        I added these method to my previous added
        OrderedMap interface:
        - SetKeys(keys []string)
        - InitValues()
        - Clone(v ...map[string]interface{}) OrderedMap
  • Loading branch information
mabels committed Oct 30, 2023
1 parent 82392f2 commit d7ddbd2
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 52 deletions.
118 changes: 66 additions & 52 deletions orderedmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,36 @@ type OrderedMap interface {
Set(key string, value interface{})
Delete(key string)
Keys() []string
SetKeys(keys []string)
Values() map[string]interface{}
InitValues()
SortKeys(sortFunc func(keys []string))
Sort(lessFunc func(a *Pair, b *Pair) bool)
UnmarshalJSON(b []byte) error
MarshalJSON() ([]byte, error)
Clone(v ...map[string]interface{}) OrderedMap
}

func New() OrderedMap {
o := OrderedMapImpl{}
o.keys = []string{}
o.values = map[string]interface{}{}
o.escapeHTML = true
return &o
return &OrderedMapImpl{
keys: make([]string, 0, 1),
values: make(map[string]interface{}, 1),
escapeHTML: true,
}
}

func (o *OrderedMapImpl) Clone(oms ...map[string]interface{}) OrderedMap {
var om map[string]interface{}
if len(oms) > 0 {
om = oms[0]
} else {
om = make(map[string]interface{})
}
return &OrderedMapImpl{
keys: make([]string, 0, len(om)),
values: om,
escapeHTML: o.escapeHTML,
}
}

func (o *OrderedMapImpl) SetEscapeHTML(on bool) {
Expand Down Expand Up @@ -93,10 +110,20 @@ func (o *OrderedMapImpl) Keys() []string {
return o.keys
}

func (o *OrderedMapImpl) SetKeys(keys []string) {
o.keys = keys
}

func (o *OrderedMapImpl) Values() map[string]interface{} {
return o.values
}

func (o *OrderedMapImpl) InitValues() {
if o.values == nil {
o.values = make(map[string]interface{})
}
}

// SortKeys Sort the map keys using your sort func
func (o *OrderedMapImpl) SortKeys(sortFunc func(keys []string)) {
sortFunc(o.keys)
Expand All @@ -116,24 +143,27 @@ func (o *OrderedMapImpl) Sort(lessFunc func(a *Pair, b *Pair) bool) {
}
}

func (o *OrderedMapImpl) UnmarshalJSON(b []byte) error {
if o.values == nil {
o.values = map[string]interface{}{}
}
err := json.Unmarshal(b, &o.values)
func BoundUnmarshalJSON(o OrderedMap, b []byte) error {
o.InitValues()
val := o.Values()
err := json.Unmarshal(b, &val)
if err != nil {
return err
}
dec := json.NewDecoder(bytes.NewReader(b))
if _, err = dec.Token(); err != nil { // skip '{'
return err
}
o.keys = make([]string, 0, len(o.values))
// o.SetKeys(make([]string, 0, len(o.Values())))
return decodeOrderedMap(dec, o)
}

func decodeOrderedMap(dec *json.Decoder, o *OrderedMapImpl) error {
hasKey := make(map[string]bool, len(o.values))
func (o *OrderedMapImpl) UnmarshalJSON(b []byte) error {
return BoundUnmarshalJSON(o, b)
}

func decodeOrderedMap(dec *json.Decoder, o OrderedMap) error {
hasKey := make(map[string]bool, len(o.Values()))
for {
token, err := dec.Token()
if err != nil {
Expand All @@ -145,16 +175,16 @@ func decodeOrderedMap(dec *json.Decoder, o *OrderedMapImpl) error {
key := token.(string)
if hasKey[key] {
// duplicate key
for j, k := range o.keys {
for j, k := range o.Keys() {
if k == key {
copy(o.keys[j:], o.keys[j+1:])
copy(o.Keys()[j:], o.Keys()[j+1:])
break
}
}
o.keys[len(o.keys)-1] = key
o.Keys()[len(o.Keys())-1] = key
} else {
hasKey[key] = true
o.keys = append(o.keys, key)
o.SetKeys(append(o.Keys(), key))
}

token, err = dec.Token()
Expand All @@ -164,43 +194,35 @@ func decodeOrderedMap(dec *json.Decoder, o *OrderedMapImpl) error {
if delim, ok := token.(json.Delim); ok {
switch delim {
case '{':
if values, ok := o.values[key].(map[string]interface{}); ok {
newMap := &OrderedMapImpl{
keys: make([]string, 0, len(values)),
values: values,
escapeHTML: o.escapeHTML,
}
if values, ok := o.Values()[key].(map[string]interface{}); ok {
newMap := o.Clone(values)
if err = decodeOrderedMap(dec, newMap); err != nil {
return err
}
o.values[key] = newMap
} else if oldMap, ok := o.values[key].(*OrderedMapImpl); ok {
newMap := &OrderedMapImpl{
keys: make([]string, 0, len(oldMap.values)),
values: oldMap.values,
escapeHTML: o.escapeHTML,
}
o.Values()[key] = newMap
} else if oldMap, ok := o.Values()[key].(OrderedMap); ok {
newMap := o.Clone(oldMap.Values())
if err = decodeOrderedMap(dec, newMap); err != nil {
return err
}
o.values[key] = newMap
} else if err = decodeOrderedMap(dec, &OrderedMapImpl{}); err != nil {
o.Values()[key] = newMap
} else if err = decodeOrderedMap(dec, o.Clone()); err != nil {
return err
}
case '[':
if values, ok := o.values[key].([]interface{}); ok {
if err = decodeSlice(dec, values, o.escapeHTML); err != nil {
if values, ok := o.Values()[key].([]interface{}); ok {
if err = decodeSlice(dec, values, o); err != nil {
return err
}
} else if err = decodeSlice(dec, []interface{}{}, o.escapeHTML); err != nil {
} else if err = decodeSlice(dec, []interface{}{}, o); err != nil {
return err
}
}
}
}
}

func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error {
func decodeSlice(dec *json.Decoder, s []interface{}, o OrderedMap) error {
for index := 0; ; index++ {
token, err := dec.Token()
if err != nil {
Expand All @@ -211,41 +233,33 @@ func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error {
case '{':
if index < len(s) {
if values, ok := s[index].(map[string]interface{}); ok {
newMap := &OrderedMapImpl{
keys: make([]string, 0, len(values)),
values: values,
escapeHTML: escapeHTML,
}
newMap := o.Clone(values)
if err = decodeOrderedMap(dec, newMap); err != nil {
return err
}
s[index] = newMap
} else if oldMap, ok := s[index].(*OrderedMapImpl); ok {
newMap := &OrderedMapImpl{
keys: make([]string, 0, len(oldMap.values)),
values: oldMap.values,
escapeHTML: escapeHTML,
}
} else if oldMap, ok := s[index].(OrderedMap); ok {
newMap := o.Clone(oldMap.Values())
if err = decodeOrderedMap(dec, newMap); err != nil {
return err
}
s[index] = newMap
} else if err = decodeOrderedMap(dec, &OrderedMapImpl{}); err != nil {
} else if err = decodeOrderedMap(dec, o.Clone()); err != nil {
return err
}
} else if err = decodeOrderedMap(dec, &OrderedMapImpl{}); err != nil {
} else if err = decodeOrderedMap(dec, o.Clone()); err != nil {
return err
}
case '[':
if index < len(s) {
if values, ok := s[index].([]interface{}); ok {
if err = decodeSlice(dec, values, escapeHTML); err != nil {
if err = decodeSlice(dec, values, o); err != nil {
return err
}
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
} else if err = decodeSlice(dec, []interface{}{}, o); err != nil {
return err
}
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
} else if err = decodeSlice(dec, []interface{}{}, o); err != nil {
return err
}
case ']':
Expand Down
152 changes: 152 additions & 0 deletions orderedmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,3 +639,155 @@ func TestMutableAfterUnmarshal(t *testing.T) {
t.Fatal("expect blabla")
}
}

type myType struct {
omap OrderedMap
}

func NewMyType() OrderedMap {
return &myType{
omap: New(),
}
}

func (j *myType) Clone(v ...map[string]interface{}) OrderedMap {
return &myType{
omap: j.omap.Clone(v...),
}
}

func (j *myType) Delete(key string) {
j.omap.Delete(key)
}

func (j *myType) SortKeys(fn func([]string)) {
j.omap.SortKeys(fn)
}
func (j *myType) Sort(fn func(*Pair, *Pair) bool) {
j.omap.Sort(fn)
}

func (j *myType) SetKeys(keys []string) {
j.omap.SetKeys(keys)
}

func (j *myType) SetEscapeHTML(bool) {
j.omap.SetEscapeHTML(true)
}

func (j *myType) InitValues() {
j.omap.InitValues()
}

// Len implements JSONProperty.
func (j *myType) Len() int {
return len(j.omap.Keys())
}

// Get implements JSONProperty.
func (j *myType) Get(key string) (interface{}, bool) {
return j.Lookup(key)
}

// Lookup implements JSONProperty.
func (j *myType) Lookup(key string) (interface{}, bool) {
out, found := j.omap.Get(key)
if !found {
return nil, false
}
isOmap, found := out.(OrderedMap)
if found {
return &myType{
omap: isOmap,
}, true
}
return out, true

}

// MarshalJSON implements JSONProperty.
func (j *myType) MarshalJSON() ([]byte, error) {
return j.omap.MarshalJSON()
}

// Set implements JSONProperty.
func (j *myType) Set(key string, value interface{}) {
j.omap.Set(key, value)
}

// UnmarshalJSON implements JSONProperty.
func (j *myType) UnmarshalJSON(b []byte) error {
return BoundUnmarshalJSON(j, b)
}

func (j *myType) Keys() []string {
return j.omap.Keys()
}

func (j *myType) Values() map[string]interface{} {
return j.omap.Values()
}

func TestClone(t *testing.T) {
const input = `{
"foo": [
{ "x": 1 },
{ "y": 2 },
"string",
4711
],
"bar": {
"x": 1
}
}`
out := NewMyType()
err := json.Unmarshal([]byte(input), &out)
if err != nil {
t.Fatal(err)
}

oarray, found := out.Get("foo")
if !found {
t.Fatal("foo is not found")
}
array, found := oarray.([]interface{})
if !found {
t.Fatal("foo is not an array")
}

if len(array) != 4 {
t.Fatal("array has not 4 elements")
}
_, found = array[0].(*myType)
if !found {
t.Fatal("array[0] is not a myType")
}
_, found = array[1].(*myType)
if !found {
t.Fatal("array[1] is not a myType")
}
if array[2].(string) != "string" {
t.Fatal("array[2] is not a string")
}
if array[3].(float64) != 4711 {
t.Fatal("array[3] is not a float64")
}
if array[0].(*myType).Keys()[0] != "x" {
t.Fatal("array[0].x is not 1")
}
if array[1].(*myType).Keys()[0] != "y" {
t.Fatal("array[1].y is not 2")
}

obar, found := out.Get("bar")
if !found {
t.Fatal("bar is not found")
}
bar, found := obar.(*myType)
if !found {
t.Fatal("bar is not a myType")
}
if bar.Keys()[0] != "x" {
t.Fatal("bar.x is not 1")
}
}

0 comments on commit d7ddbd2

Please sign in to comment.