flatjson

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2024 License: MIT Imports: 7 Imported by: 0

README

flatjson

Go Reference

flatjson is a Go package that provides utilities for flattening nested JSON structures into a sequence of path-value pairs. This makes it easier to compare differences between JSON objects, which is useful for various purposes such as data synchronization, version control, and data analysis.

Installation

go install github.com/bgpat/flatjson/cmd/flatjson

Example

Flatten
$ cat << EOF > input.json
{
  "a": true,
  "b": {
    "c": 2
  },
  "d": [
    "foo",
    "bar"
  ]
}
EOF
$ flatjson < input.json
[
  {
    "path": [],
    "value": {"a": true, "b": {"c": 2}, "d": ["foo", "bar"]}
  },
  {
    "path": ["a"],
    "value": true
  },
  {
    "path": ["b"],
    "value": {"c": 2}
  },
  {
    "path": ["b", "c"],
    "value": 2
  },
  {
    "path": ["d"],
    "value": ["foo", "bar"]
  },
  {
    "path": ["d", 0],
    "value": "foo"
  },
  {
    "path": ["d", 1],
    "value": "bar"
  }
]
Diff
$ jq 'del(.a) | .d[1] = "buzz" | .e = null' input.json | flatjson -diff input.json -
[
  {
    "type": "replace",
    "path": [],
    "value": {"b": {"c": 2}, "d": ["foo", "buzz"], "e": null},
    "old_value": {"a": true, "b": {"c": 2}, "d": ["foo", "bar"]}
  },
  {
    "type": "remove",
    "path": ["a"]
  },
  {
    "type": "replace",
    "path": ["d"],
    "value": ["foo", "buzz"],
    "old_value": ["foo", "bar"]
  },
  {
    "type": "replace",
    "path": ["d", 1],
    "value": "buzz",
    "old_value": "bar"
  },
  {
    "type": "add",
    "path": ["e"],
    "value": null
  }
]

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DiffOperation added in v0.2.0

type DiffOperation struct {
	Type     DiffOperationType `json:"type"`
	Path     Path              `json:"path"`
	Value    any               `json:"value"`
	OldValue any               `json:"old_value"`
}

DiffOperation represents a difference operation between two JSON objects.

Value is only set if the type is add or replace, and OldValue is only set if the type is replace.

func Diff added in v0.2.0

func Diff(x, y any) ([]DiffOperation, error)

Diff computes the differences between two JSON objects, returning operations that represent them.

Errors may occur during marshaling and unmarshaling.

Example
package main

import (
	"fmt"

	"github.com/bgpat/flatjson"
)

var (
	json1 = map[string]any{
		"name": "Alice",
		"age":  30,
		"address": map[string]any{
			"city": "Tokyo",
			"zip":  "100-0001",
		},
	}
	json2 = map[string]any{
		"name": "Alice",
		"age":  31,
		"address": map[string]any{
			"city":    "Kyoto",
			"country": "Japan",
		},
	}
)

func main() {
	diff, _ := flatjson.Diff(json1, json2)
	for _, op := range diff {
		switch op.Type {
		case flatjson.DiffOperationTypeAdd:
			fmt.Printf("%v %q: %v\n", op.Type, op.Path, op.Value)
		case flatjson.DiffOperationTypeRemove:
			fmt.Printf("%v %q\n", op.Type, op.Path)
		case flatjson.DiffOperationTypeReplace:
			fmt.Printf("%v %q: %v => %v\n", op.Type, op.Path, op.OldValue, op.Value)
		}
	}
}
Output:

replace []: map[address:map[city:Tokyo zip:100-0001] age:30 name:Alice] => map[address:map[city:Kyoto country:Japan] age:31 name:Alice]
replace ["address"]: map[city:Tokyo zip:100-0001] => map[city:Kyoto country:Japan]
replace ["address" "city"]: Tokyo => Kyoto
add ["address" "country"]: Japan
remove ["address" "zip"]
replace ["age"]: 30 => 31

func (DiffOperation) MarshalJSON added in v0.2.0

func (d DiffOperation) MarshalJSON() ([]byte, error)

MarshalJSON implements encoding/json.Marshaler.

type DiffOperationType added in v0.2.0

type DiffOperationType string

DiffOperationType defines the type of operations used in diffs.

const (
	DiffOperationTypeAdd     DiffOperationType = "add"
	DiffOperationTypeRemove  DiffOperationType = "remove"
	DiffOperationTypeReplace DiffOperationType = "replace"
)

Operation types used in diffs. These are based on the `op` field in JSON Patch (RFC 6902).

type FlatJSON

type FlatJSON []PathValue

FlatJSON represents a flattened JSON structure as a slice of PathValue.

func Flatten

func Flatten(target any) (FlatJSON, error)

Flatten takes a nested JSON structure and returns flattened path-value pairs, sorted by the paths. The target can be any JSON-serializable object.

Example
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/bgpat/flatjson"
)

func main() {
	obj := map[string]any{
		"a": true,
		"b": map[string]any{
			"c": 2,
		},
		"d": []string{
			"foo",
			"bar",
		},
	}
	flat, err := flatjson.Flatten(obj)
	if err != nil {
		log.Fatal(err)
	}
	for _, pv := range flat {
		path, err := json.Marshal(pv.Path)
		if err != nil {
			log.Fatal(err)
		}
		value, err := json.Marshal(pv.Value)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s = %s\n", path, value)
	}
}
Output:

[] = {"a":true,"b":{"c":2},"d":["foo","bar"]}
["a"] = true
["b"] = {"c":2}
["b","c"] = 2
["d"] = ["foo","bar"]
["d",0] = "foo"
["d",1] = "bar"

func (FlatJSON) Diff added in v0.2.0

func (f FlatJSON) Diff(x FlatJSON) []DiffOperation

Diff computes the differences between this and another FlatJSON, returning operations that represent them.

func (FlatJSON) Get

func (f FlatJSON) Get(p Path) (any, bool)

Get retrieves the value at the given path and returns it with whether it was found.

Example
package main

import (
	"fmt"

	"github.com/bgpat/flatjson"
)

func main() {
	flat := flatjson.FlatJSON{
		{Path: flatjson.Path{"a"}, Value: 1},
		{Path: flatjson.Path{"b", "c"}, Value: 2},
	}
	if value, found := flat.Get(flatjson.Path{"b", "c"}); found {
		fmt.Println(value)
	}
}
Output:

2

type Path

type Path []any

Path represents a sequence of elements that form a JSON path.

The names in the Path are either string or int. A string represents the name of an object member, while an int represents an array index.

Example
package main

import (
	"fmt"

	"github.com/bgpat/flatjson"
)

func main() {
	p := flatjson.Path{"store", "book", 0}
	title := p.Join("title")
	fmt.Printf("%#v\n", title)
	fmt.Printf("%q\n", title.JSONPointer())
}
Output:

flatjson.Path{"store", "book", 0, "title"}
"/store/book/0/title"

func (Path) JSONPointer

func (p Path) JSONPointer() string

JSONPointer returns the JSON Pointer (RFC 6091) representation as a string.

func (Path) Join

func (p Path) Join(name any) Path

Join adds a name and returns the new Path.

type PathValue

type PathValue struct {
	Path  Path `json:"path"`
	Value any  `json:"value"`
}

PathValue represents a single path-value pair in the flattened JSON structure.

Directories

Path Synopsis
cmd
flatjson command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL