Skip to content

Instantly share code, notes, and snippets.

@awalterschulze
Created March 19, 2018 18:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save awalterschulze/86401e1080dfa2d7fc86ed0991146b52 to your computer and use it in GitHub Desktop.
Save awalterschulze/86401e1080dfa2d7fc86ed0991146b52 to your computer and use it in GitHub Desktop.
flatten and unflatten a json structure for a given field name.
package main
import (
"encoding/base64"
gojson "encoding/json"
"fmt"
"io"
"github.com/katydid/katydid/parser"
"github.com/katydid/katydid/parser/json"
)
func main() {
lifted := `{"town":{"postcode":"12345"}}`
flattened := `{"postcode":"12345"}`
parser := json.NewJsonParser()
if err := parser.Init([]byte(lifted)); err != nil {
panic(err)
}
out, err := Flatten(parser, "town")
if err != nil {
panic(err)
}
if string(out) != flattened {
fmt.Printf("got %s want %s\n", out, flattened)
}
parser = json.NewJsonParser()
if err := parser.Init([]byte(flattened)); err != nil {
panic(err)
}
out, err = Lift(parser, "postcode", "town")
if err != nil {
panic(err)
}
if string(out) != lifted {
fmt.Printf("got %s want %s\n", out, lifted)
}
}
func Flatten(p parser.Interface, name string) ([]byte, error) {
m, err := flatten(p, name)
if err != nil {
return nil, err
}
buf, err := gojson.Marshal(m)
return buf, err
}
func flatten(p parser.Interface, toFlatten string) (interface{}, error) {
var fields = make(map[string]interface{})
var list []interface{}
for {
if err := p.Next(); err != nil {
if err == io.EOF {
if len(fields) > 0 {
return fields, nil
}
if list != nil {
return list, nil
}
return nil, nil
}
return nil, err
}
if p.IsLeaf() {
if v, err := p.Int(); err == nil {
return v, nil
}
if v, err := p.Uint(); err == nil {
return v, nil
}
if v, err := p.Double(); err == nil {
return v, nil
}
if v, err := p.Bool(); err == nil {
return v, nil
}
if v, err := p.String(); err == nil {
return v, nil
}
if v, err := p.Bytes(); err == nil {
return base64.StdEncoding.EncodeToString(v), nil
}
return nil, nil
}
// arrays
if _, err := p.Int(); err == nil {
p.Down()
item, err := flatten(p, toFlatten)
if err != nil {
return nil, err
}
p.Up()
list = append(list, item)
continue
}
// fields
name, err := p.String()
if err != nil {
return nil, err
}
p.Down()
value, err := flatten(p, toFlatten)
if err != nil {
return nil, err
}
p.Up()
if name == toFlatten {
if m, ok := value.(map[string]interface{}); ok {
for k, v := range m {
fields[k] = v
}
}
} else {
fields[name] = value
}
}
}
func Lift(p parser.Interface, from, to string) ([]byte, error) {
m, err := lift(p, from, to)
if err != nil {
return nil, err
}
buf, err := gojson.Marshal(m)
return buf, err
}
func lift(p parser.Interface, from, to string) (interface{}, error) {
var fields = make(map[string]interface{})
var list []interface{}
for {
if err := p.Next(); err != nil {
if err == io.EOF {
if len(fields) > 0 {
return fields, nil
}
if list != nil {
return list, nil
}
return nil, nil
}
return nil, err
}
if p.IsLeaf() {
if v, err := p.Int(); err == nil {
return v, nil
}
if v, err := p.Uint(); err == nil {
return v, nil
}
if v, err := p.Double(); err == nil {
return v, nil
}
if v, err := p.Bool(); err == nil {
return v, nil
}
if v, err := p.String(); err == nil {
return v, nil
}
if v, err := p.Bytes(); err == nil {
return base64.StdEncoding.EncodeToString(v), nil
}
return nil, nil
}
// arrays
if _, err := p.Int(); err == nil {
p.Down()
item, err := lift(p, from, to)
if err != nil {
return nil, err
}
p.Up()
list = append(list, item)
continue
}
// fields
name, err := p.String()
if err != nil {
return nil, err
}
p.Down()
value, err := lift(p, from, to)
if err != nil {
return nil, err
}
p.Up()
if name == from {
fields[to] = map[string]interface{}{from: value}
} else {
fields[name] = value
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment