I am writing a trie DS, json gzipped into a file trieSample.json.gz
, and reading it back into the struct. Strangely, the unmarshal succeeds but the struct is not populated.
I have tried both json.Unmarshal and json.Decoder to no avail. Need help in finding what I am missing here. There is no error thrown while reading, just that the struct doesn't have any keys. If I try normal json marshal -> Write to file and Read from file -> Unmarshal, it works properly.
var charSet = "0123456789bcdefghjkmnopqrstuvwxyz"
const logTagSlice = "trie.log"
type trieSlice struct {
Children []*tNode `json:"c"`
Charset map[int32]int8 `json:"l"` // Charset is the legend of what charset is used to create the keys and how to position them in trie array
logger loggingapi.Logger `json:"-"`
capacity int `json:"-"` // capacity is the slice capacity to have enough to hold all the characters in the charset
}
type tNode struct {
Children []*tNode `json:"c"` // children represents the next valid runes AFTER the current one
IsLeaf bool `json:"e,omitempty"` // isLeaf represents if this node represents the end of a valid word
Value int16 `json:"v,omitempty"` // value holds the corresponding value for key value pair, where key is the whole tree of nodes starting from parent representing a valid word
}
// NewTrieSlice returns a Trie, charset represents how the children need to be positioned in the array
func NewTrieSlice(charset string, logger loggingapi.Logger) *trieSlice {
m := map[int32]int8{}
for index, r := range charset {
m[r] = int8(index)
}
return &trieSlice{
Charset: m,
Children: make([]*tNode, len(charset)),
logger: logger,
capacity: len(charset),
}
}
func newNode(capacity int) *tNode {
return &tNode{
Children: make([]*tNode, capacity),
}
}
// getPosition gets the array index position that the rune should be put in
func (t *trieSlice) getPosition(r int32) (index int8, found bool) {
if index, ok := t.Charset[r]; ok {
return index, true
}
return -1, false
}
// Add ...
func (t *trieSlice) Add(key string, val int16) {
if len(key) == 0 {
t.logger.Info(logTagSlice, "trying to add empty key, return with no action")
return
}
runes := []rune(key)
prefix := runes[0]
var child *tNode
var pos int
index, ok := t.getPosition(prefix)
if !ok {
t.logger.Info(logTagSlice, "key is not present in the charset %s, cannot add to trieSlice", prefix)
return
}
// trie node with same prefix doesnt exist
if child = t.Children[index]; child == nil {
child = newNode(len(t.Charset))
t.Children[index] = child
}
pos = 1
for pos <= len(runes) {
// base condition
if pos == len(key) {
child.IsLeaf = true
child.Value = val
return
}
prefix := runes[pos]
index, ok := t.getPosition(prefix)
if !ok {
t.logger.Info(logTagSlice, "key is not present in the charset %s, cannot add to trieSlice", prefix)
return
}
// repeat with child node if prefix is already present
if newChild := child.Children[index]; newChild == nil {
child.Children[index] = newNode(len(t.Charset))
child = child.Children[index]
} else {
child = newChild
}
pos++
}
}
// Test using gzip writer, reader
func TestSample(t *testing.T) {
// Create trie and add a few keys
trie := NewTrieSlice(charSet, loggingapi.NewStdOut())
trie.Add("test", 10)
trie.Add("test1", 20)
trie.Add("test2", 30)
trie.Add("test3", 40)
trie.Add("test4", 50)
// Write gzipped json to file
var network bytes.Buffer
b, err := json.Marshal(trie)
if err != nil {
fmt.Println("error in marshal ... ", err.Error())
t.Fail()
}
w := gzip.NewWriter(&network)
w.Write(b)
ioutil.WriteFile("../resources/trieSample.json.gz", []byte(network.String()), 0644)
w.Close()
// Read gzipped json from file into struct
trieUnmarshal := NewTrieSlice(charSet, loggingapi.NewStdOut())
trieDecoder := NewTrieSlice(charSet, loggingapi.NewStdOut())
// attempt via json Unmarshal
file, err := os.Open("../resources/trieSample.json.gz")
if err != nil {
fmt.Println(err.Error())
t.Fail()
}
r, err := gzip.NewReader(file)
if err != nil {
fmt.Println(err.Error())
t.Fail()
}
sc := bufio.NewScanner(r)
json.Unmarshal(sc.Bytes(), trieUnmarshal)
// attempt via json Decoder
b, err = ioutil.ReadFile("../resources/trieSample.json.gz")
if err != nil {
fmt.Println(err.Error())
t.Fail()
}
bReader := bytes.NewReader(b)
json.NewDecoder(bReader).Decode(trieDecoder)
// spew.Dump shows that object is not populated
spew.Dump(trieUnmarshal)
spew.Dump(trieDecoder)
}
spew.Dump shows that the trieSlice Children array has all nil elements