Skip to content

Commit

Permalink
Merge pull request #53 from emirpasic/json
Browse files Browse the repository at this point in the history
JSON serialization (marshaling) / deserialization (unmarshaling)
  • Loading branch information
emirpasic authored Mar 6, 2017
2 parents 51d19e7 + febc429 commit f6c17b5
Show file tree
Hide file tree
Showing 36 changed files with 1,231 additions and 15 deletions.
130 changes: 115 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ Implementation of various data structures and algorithms in Go.
- [Enumerable](#enumerable)
- [EnumerableWithIndex](#enumerablewithindex)
- [EnumerableWithKey](#enumerablewithkey)
- [Serialization](#serialization)
- [JSONSerializer](#jsonserializer)
- [JSONDeserializer](#jsondeserializer)
- [Sort](#sort)
- [Container](#container)
- [Appendix](#appendix)
Expand Down Expand Up @@ -104,7 +107,7 @@ type List interface {

A [list](#lists) backed by a dynamic array that grows and shrinks implicitly.

Implements [List](#lists), [IteratorWithIndex](#iteratorwithindex) and [EnumerableWithIndex](#enumerablewithindex) interfaces.
Implements [List](#lists), [IteratorWithIndex](#iteratorwithindex), [EnumerableWithIndex](#enumerablewithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -141,7 +144,7 @@ func main() {

A [list](#lists) where each element points to the next element in the list.

Implements [List](#lists), [IteratorWithIndex](#iteratorwithindex) and [EnumerableWithIndex](#enumerablewithindex) interfaces.
Implements [List](#lists), [IteratorWithIndex](#iteratorwithindex), [EnumerableWithIndex](#enumerablewithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -178,7 +181,7 @@ func main() {

A [list](#lists) where each element points to the next and previous elements in the list.

Implements [List](#lists), [IteratorWithIndex](#iteratorwithindex) and [EnumerableWithIndex](#enumerablewithindex) interfaces.
Implements [List](#lists), [IteratorWithIndex](#iteratorwithindex), [EnumerableWithIndex](#enumerablewithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -235,7 +238,7 @@ type Set interface {

A [set](#sets) backed by a hash table (actually a Go's map). It makes no guarantees as to the iteration order of the set.

Implements [Set](#sets) interface.
Implements [Set](#sets), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand All @@ -262,7 +265,7 @@ func main() {

A [set](#sets) backed by a [red-black tree](#redblacktree) to keep the elements ordered with respect to the [comparator](#comparator).

Implements [Set](#sets), [IteratorWithIndex](#iteratorwithindex) and [EnumerableWithIndex](#enumerablewithindex) interfaces.
Implements [Set](#sets), [IteratorWithIndex](#iteratorwithindex), [EnumerableWithIndex](#enumerablewithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -309,7 +312,7 @@ type Stack interface {

A [stack](#stacks) based on a [linked list](#singlylinkedlist).

Implements [Stack](#stacks) and [IteratorWithIndex](#iteratorwithindex) interfaces.
Implements [Stack](#stacks), [IteratorWithIndex](#iteratorwithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand All @@ -336,7 +339,7 @@ func main() {

A [stack](#stacks) based on a [array list](#arraylist).

Implements [Stack](#stacks) and [IteratorWithIndex](#iteratorwithindex) interfaces.
Implements [Stack](#stacks), [IteratorWithIndex](#iteratorwithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -394,7 +397,7 @@ type BidiMap interface {

A [map](#maps) based on hash tables. Keys are unordered.

Implements [Map](#maps) interface.
Implements [Map](#maps), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand All @@ -421,7 +424,7 @@ func main() {

A [map](#maps) based on [red-black tree](#redblacktree). Keys are ordered ordered with respect to the [comparator](#comparator).

Implements [Map](#maps), [IteratorWithKey](#iteratorwithkey) and [EnumerableWithKey](#enumerablewithkey) interfaces.
Implements [Map](#maps), [IteratorWithKey](#iteratorwithkey), [EnumerableWithKey](#enumerablewithkey), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -452,7 +455,7 @@ func main() {

A [map](#maps) based on two hashmaps. Keys are unordered.

Implements [BidiMap](#maps) interface.
Implements [BidiMap](#maps), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -481,7 +484,7 @@ func main() {

A [map](#maps) based on red-black tree. This map guarantees that the map will be in both ascending key and value order. Other than key and value ordering, the goal with this structure is to avoid duplication of elements (unlike in [HashBidiMap](#hashbidimap)), which can be significant if contained elements are large.

Implements [BidiMap](#maps), [IteratorWithKey](#iteratorwithkey) and [EnumerableWithKey](#enumerablewithkey) interfaces.
Implements [BidiMap](#maps), [IteratorWithKey](#iteratorwithkey), [EnumerableWithKey](#enumerablewithkey), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main
Expand Down Expand Up @@ -531,7 +534,7 @@ A red–black [tree](#trees) is a binary search tree with an extra bit of data p

The balancing of the tree is not perfect but it is good enough to allow it to guarantee searching in O(log n) time, where n is the total number of elements in the tree. The insertion and deletion operations, along with the tree rearrangement and recoloring, are also performed in O(log n) time. <sub><sup>[Wikipedia](http://en.wikipedia.org/wiki/Red%E2%80%93black_tree)</sup></sub>

Implements [Tree](#trees) and [ReverseIteratorWithKey](#reverseiteratorwithkey) interfaces.
Implements [Tree](#trees), [ReverseIteratorWithKey](#reverseiteratorwithkey), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

<p align="center"><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Red-black_tree_example.svg/500px-Red-black_tree_example.svg.png" width="400px" height="200px" /></p>

Expand Down Expand Up @@ -597,7 +600,7 @@ AVL [tree](#trees) is a self-balancing binary search tree. In an AVL tree, the h

AVL trees are often compared with red–black trees because both support the same set of operations and take O(log n) time for the basic operations. For lookup-intensive applications, AVL trees are faster than red–black trees because they are more strictly balanced. <sub><sup>[Wikipedia](https://en.wikipedia.org/wiki/AVL_tree)</sup></sub>

Implements [Tree](#trees) and [ReverseIteratorWithKey](#reverseiteratorwithkey) interfaces.
Implements [Tree](#trees), [ReverseIteratorWithKey](#reverseiteratorwithkey), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

<p align="center"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/ad/AVL-tree-wBalance_K.svg/262px-AVL-tree-wBalance_K.svg.png" width="300px" height="180px" /><br/><sub>AVL tree with balance factors (green)</sub></p>

Expand Down Expand Up @@ -664,7 +667,7 @@ According to Knuth's definition, a B-tree of order m is a tree which satisfies t

Each internal node’s keys act as separation values which divide its subtrees. For example, if an internal node has 3 child nodes (or subtrees) then it must have 2 keys: a1 and a2. All values in the leftmost subtree will be less than a1, all values in the middle subtree will be between a1 and a2, and all values in the rightmost subtree will be greater than a2.<sub><sup>[Wikipedia](http://en.wikipedia.org/wiki/Red%E2%80%93black_tree)</sub></sup>

Implements [Tree](#trees) and [ReverseIteratorWithKey](#reverseiteratorwithkey) interfaces.
Implements [Tree](#trees), [ReverseIteratorWithKey](#reverseiteratorwithkey), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

<p align="center"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/B-tree.svg/831px-B-tree.svg.png" width="400px" height="111px" /></p>

Expand Down Expand Up @@ -736,7 +739,7 @@ A binary heap is a [tree](#trees) created using a binary tree. It can be seen as

All nodes are either greater than or equal to or less than or equal to each of its children, according to a comparison predicate defined for the heap. <sub><sup>[Wikipedia](http://en.wikipedia.org/wiki/Binary_heap)</sub></sup>

Implements [Tree](#trees) and [ReverseIteratorWithIndex](#reverseiteratorwithindex) interfaces.
Implements [Tree](#trees), [ReverseIteratorWithIndex](#reverseiteratorwithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

<p align="center"><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Max-Heap.svg/501px-Max-Heap.svg.png" width="300px" height="200px" /></p>

Expand Down Expand Up @@ -1214,6 +1217,103 @@ func main() {
}
```

### Serialization

All data structures can be serialized (marshalled) and deserialized (unmarshalled). Currently only JSON support is available.

#### JSONSerializer

Outputs the container into its JSON representation.

Typical usage for key-value structures:
```go
package main

import (
"fmt"
"github.com/emirpasic/gods/maps/hashmap"
)

func main() {
m := hashmap.New()
m.Put("a", "1")
m.Put("b", "2")
m.Put("c", "3")

json, err := m.ToJSON()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(json)) // {"a":"1","b":"2","c":"3"}
```
Typical usage for value-only structures:
```go
package main

import (
"fmt"
"github.com/emirpasic/gods/lists/arraylist"
)

func main() {
list := arraylist.New()
list.Add("a", "b", "c")

json, err := list.ToJSON()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(json)) // ["a","b","c"]
}
```
#### JSONDeserializer
Populates the container with elements from the input JSON representation.
Typical usage for key-value structures:
```go
package main

import (
"fmt"
"github.com/emirpasic/gods/lists/arraylist"
)

func main() {
list := arraylist.New()

json := []byte(`["a","b"]`)
err := list.FromJSON(json)
if err != nil {
fmt.Println(err)
}
fmt.Println(list) // ArrayList ["a","b"]
}
```
Typical usage for value-only structures:
```go
package main

import (
"fmt"
"github.com/emirpasic/gods/lists/arraylist"
)

func main() {
list := arraylist.New()

json := []byte(`["a","b"]`)
err := list.FromJSON(json)
if err != nil {
fmt.Println(err)
}
fmt.Println(list) // ArrayList ["a","b"]
}
```
### Sort
Sort is a general purpose sort function.
Expand Down
2 changes: 2 additions & 0 deletions containers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
// Iterators provide stateful iterators.
//
// Enumerable provides Ruby inspired (each, select, map, find, any?, etc.) container functions.
//
// Serialization provides serializers (marshalers) and deserializers (unmarshalers).
package containers

import "github.com/emirpasic/gods/utils"
Expand Down
17 changes: 17 additions & 0 deletions containers/serialization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package containers

// JSONSerializer provides JSON serialization
type JSONSerializer interface {
// ToJSON outputs the JSON representation of containers's elements.
ToJSON() ([]byte, error)
}

// JSONDeserializer provides JSON deserialization
type JSONDeserializer interface {
// FromJSON populates containers's elements from the input JSON representation.
FromJSON([]byte) error
}
51 changes: 51 additions & 0 deletions examples/serialization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package examples

import (
"fmt"
"github.com/emirpasic/gods/lists/arraylist"
"github.com/emirpasic/gods/maps/hashmap"
)

// ListSerializationExample demonstrates how to serialize and deserialize lists to and from JSON
func ListSerializationExample() {
list := arraylist.New()
list.Add("a", "b", "c")

// Serialization (marshalling)
json, err := list.ToJSON()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(json)) // ["a","b","c"]

// Deserialization (unmarshalling)
json = []byte(`["a","b"]`)
err = list.FromJSON(json)
if err != nil {
fmt.Println(err)
}
fmt.Println(list) // ArrayList ["a","b"]
}

// MapSerializationExample demonstrates how to serialize and deserialize maps to and from JSON
func MapSerializationExample() {
m := hashmap.New()
m.Put("a", "1")
m.Put("b", "2")
m.Put("c", "3")

// Serialization (marshalling)
json, err := m.ToJSON()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(json)) // {"a":"1","b":"2","c":"3"}

// Deserialization (unmarshalling)
json = []byte(`{"a":"1","b":"2"}`)
err = m.FromJSON(json)
if err != nil {
fmt.Println(err)
}
fmt.Println(m) // HashMap {"a":"1","b":"2"}
}
26 changes: 26 additions & 0 deletions lists/arraylist/arraylist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,32 @@ func TestListIteratorLast(t *testing.T) {
}
}

func TestListSerialization(t *testing.T) {
list := New()
list.Add("a", "b", "c")

var err error
assert := func() {
if actualValue, expectedValue := fmt.Sprintf("%s%s%s", list.Values()...), "abc"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := list.Size(), 3; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if err != nil {
t.Errorf("Got error %v", err)
}
}

assert()

json, err := list.ToJSON()
assert()

err = list.FromJSON(json)
assert()
}

func benchmarkGet(b *testing.B, list *List, size int) {
for i := 0; i < b.N; i++ {
for n := 0; n < size; n++ {
Expand Down
29 changes: 29 additions & 0 deletions lists/arraylist/serialization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package arraylist

import (
"encoding/json"
"github.com/emirpasic/gods/containers"
)

func assertSerializationImplementation() {
var _ containers.JSONSerializer = (*List)(nil)
var _ containers.JSONDeserializer = (*List)(nil)
}

// ToJSON outputs the JSON representation of list's elements.
func (list *List) ToJSON() ([]byte, error) {
return json.Marshal(list.elements[:list.size])
}

// FromJSON populates list's elements from the input JSON representation.
func (list *List) FromJSON(data []byte) error {
err := json.Unmarshal(data, &list.elements)
if err == nil {
list.size = len(list.elements)
}
return err
}
Loading

0 comments on commit f6c17b5

Please sign in to comment.