Categorygithub.com/jarxorg/tree
modulepackage
0.8.0
Repository: https://github.com/jarxorg/tree.git
Documentation: pkg.go.dev

# README

Tree

PkgGoDev Report Card

Tree is a simple structure for dealing with dynamic or unknown JSON/YAML in Go.

Features

  • Parses json/yaml of unknown structure to get to nodes with fluent interface.
  • Syntax similar to Go standard and map and slice.
  • Find function can be specified the Query expression.
  • Edit function can be specified the Edit expression.
  • Bundled 'tq' that is a portable command-line JSON/YAML processor.

Road to 1.0

  • Placeholders in query.
  • Merging nodes.

Syntax

Go

tree.Map{
	"ID":     tree.ToValue(1),
	"Name":   tree.ToValue("Reds"),
	"Colors": tree.ToArrayValues("Crimson", "Red", "Ruby", "Maroon"),
}

JSON

{
	"ID": 1,
	"Name": "Reds",
	"Colors": ["Crimson", "Red", "Ruby", "Maroon"]
}

YAML

ID: 1
Name: Reds
Colors:
- Crimson
- Red
- Ruby
- Maroon

Marshal and Unmarshal

func ExampleMarshalJSON() {
	group := tree.Map{
		"ID":     tree.ToValue(1),
		"Name":   tree.ToValue("Reds"),
		"Colors": tree.ToArrayValues("Crimson", "Red", "Ruby", "Maroon"),
	}
	b, err := json.Marshal(group)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(b))

	// Output:
	// {"Colors":["Crimson","Red","Ruby","Maroon"],"ID":1,"Name":"Reds"}
}
func ExampleUnmarshalJSON() {
	data := []byte(`[
  {"Name": "Platypus", "Order": "Monotremata"},
  {"Name": "Quoll",    "Order": "Dasyuromorphia"}
]`)

	var animals tree.Array
	err := json.Unmarshal(data, &animals)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", animals)

	// Output:
	// [map[Name:Platypus Order:Monotremata] map[Name:Quoll Order:Dasyuromorphia]]
}

Using other parsers

Tree may works on other parsers those has compatible with "encoding/json" or "gopkg.in/yaml.v2". See examples directory.

Alternate json.RawMessage

For example, Dynamic JSON in Go shows an example of using json.RawMessage.

It may be simpler to use tree.Map instead of json.RawMessage.

package main

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

	"github.com/jarxorg/tree"
)

const input = `
{
	"type": "sound",
	"msg": {
		"description": "dynamite",
		"authority": "the Bruce Dickinson"
	}
}
`

type Envelope struct {
	Type string
	Msg  tree.Map
}

func main() {
	env := Envelope{}
	if err := json.Unmarshal([]byte(input), &env); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%#v\n", env)
	fmt.Printf("%#v\n", env.Msg.Get("description"))

	// Output:
	// main.Envelope{Type:"sound", Msg:tree.Map{"authority":"the Bruce Dickinson", "description":"dynamite"}}
	// "dynamite"
}

Get

func ExampleGet() {
	group := tree.Map{
		"ID":     tree.ToValue(1),
		"Name":   tree.ToValue("Reds"),
		"Colors": tree.ToArrayValues("Crimson", "Red", "Ruby", "Maroon"),
		"Nil":    nil,
	}
	fmt.Println(group.Get("Colors").Get(1))
	fmt.Println(group.Get("Colors", 2))
	fmt.Println(group.Get("Colors").Get(5).IsNil())
	fmt.Println(group.Get("Nil").IsNil())

	// Output:
	// Red
	// Ruby
	// true
	// true
}

Find

func ExampleFind() {
	group := tree.Map{
		"ID":     tree.ToValue(1),
		"Name":   tree.ToValue("Reds"),
		"Colors": tree.ToArrayValues("Crimson", "Red", "Ruby", "Maroon"),
	}

	rs, err := group.Find(".Colors[1:3]")
	if err != nil {
		log.Fatal(err)
	}
	for _, r := range rs {
		fmt.Println(r)
	}

	// Output:
	// Red
	// Ruby
}

Query

QueryDescriptionResults
.store.book[0]The first book{"category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95}
.store.book[0].priceThe price of the first book8.95
.store.book.0.priceThe price of the first book (using dot)8.95
.store.book[:2].priceAll prices of books[0:2] (index 2 is exclusive)8.95, 12.99
.store.book[].authorAll authors of all books"Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"
..authorAll authors"Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"
..author | [0]The first author"Nigel Rees"
.store.book[(.category == "fiction" or .category == "reference") and .price < 10].titleAll titles of books these are categoried into "fiction", "reference" and price < 10"Sayings of the Century", "Moby Dick"
.store.book[.title ~= "^S"].titleTitles beginning with "S""Sayings of the Century", "Sword of Honour"
.store.book.count()Count books4
.store.book[0].keys()Sorted keys of the first book["author", "category", "price", "title"]
.store.book[0].values()Values of the first book["Nigel Rees", "reference", 8.95, "Sayings of the Century"]

Illustrative Object

{
  "store": {
    "book": [{
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

Edit

func ExampleEdit() {
	var group tree.Node = tree.Map{
		"ID":     tree.ToValue(1),
		"Name":   tree.ToValue("Reds"),
		"Colors": tree.ToArrayValues("Crimson", "Red", "Ruby", "Maroon"),
	}

	if err := tree.Edit(&group, ".Colors += \"Pink\""); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Append Pink to Colors:\n  %+v\n", group)

	if err := tree.Edit(&group, ".Name = \"Blue\""); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Set Blue to Name:\n  %+v\n", group)

	if err := tree.Edit(&group, ".Colors ^?"); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Delete Colors:\n  %+v\n", group)

	// Output:
	// Append Pink to Colors:
	//   map[Colors:[Crimson Red Ruby Maroon Pink] ID:1 Name:Reds]
	// Set Blue to Name:
	//   map[Colors:[Crimson Red Ruby Maroon Pink] ID:1 Name:Blue]
	// Delete Colors:
	//   map[ID:1 Name:Blue]
}

tq

tq is a portable command-line JSON/YAML processor.

Installation

go install github.com/jarxorg/tree/cmd/tq@latest

Using Homebrew

brew tap jarxorg/tree
brew install jarxorg/tree/tq

Download binary

VERSION=0.8.0 GOOS=Darwin GOARCH=arm64; curl -fsSL "https://github.com/jarxorg/tree/releases/download/v${VERSION}/tree_${VERSION}_${GOOS}_${GOARCH}.tar.gz" | tar xz tq && mv tq /usr/local/bin

Usage

tq is a command-line JSON/YAML processor.

Usage:
  tq [flags] [query] ([file...])

Flags:
  -c, --color                  output with colors
  -e, --edit stringArray       edit expression
  -x, --expand                 expand results
  -h, --help                   help for tq
  -U, --inplace                update files, inplace
  -i, --input-format string    input format (json or yaml)
  -j, --input-json             alias --input-format json
  -y, --input-yaml             alias --input-format yaml
  -O, --output string          output file
  -o, --output-format string   output format (json or yaml, default json)
  -J, --output-json            alias --output-format json
  -Y, --output-yaml            alias --output-format yaml
  -r, --raw                    output raw strings
  -s, --slurp                  slurp all results into an array
  -t, --template string        golang text/template string
  -v, --version                print version

Examples:
  % echo '{"colors": ["red", "green", "blue"]}' | tq '.colors[0]'
  "red"

  % echo '{"users":[{"id":1,"name":"one"},{"id":2,"name":"two"}]}' | tq -x -t '{{.id}}: {{.name}}' '.users'
  1: one
  2: two

  % echo '{}' | tq -e '.colors = ["red", "green"]' -e '.colors += "blue"' .
  {
    "colors": [
      "red",
      "green",
      "blue"
    ]
  }

for jq user

tqjq
tq '.store.book[0]'jq '.store.book[0]'
tq '.store.book[]'jq '.store.book[]'
tq '.store.book[:2].price'jq '.store.book[:2][] | .price'
tq '.store.book[.category == "fiction" and .price < 10].title'jq '.store.book[] | select(.category == "fiction" and .price < 10) | .title'

Third-party library licenses

# Packages

No description provided by the author

# Functions

DecodeJSON decodes JSON as a node using the provided decoder.
DecodeYAML decodes YAML as a node using the provided decoder.
No description provided by the author
Find finds a node from n using the Query.
MarshalJSON returns the JSON encoding of the specified node.
MarshalViaJSON returns the node encoding of v via "encoding/json".
MarshalViaYAML returns the node encoding of v via "gopkg.in/yaml.v2".
MarshalYAML returns the YAML encoding of the specified node.
OutputColorJSON writes JSON values with color to out.
OutputColorYAML writes YAML values with color to out.
ParseQuery parses the provided expr to a Query.
No description provided by the author
ToArrayValues calss ToValues for each provided vs and returns them as an Array.
ToNode converts the specified v to an Node.
ToNodeValues calss ToValues for each provided vs and returns them as []Node.
ToValue converts the specified v to a Value as Node.
UnmarshalJSON parses the JSON-encoded data to a Node.
UnmarshalViaJSON stores the node in the value pointed to by v via "encoding/json".
UnmarshalViaYAML stores the node in the value pointed to by v via "gopkg.in/yaml.v2".
UnmarshalYAML returns the YAML encoding of the specified node.
Walk walks the node tree rooted at root, calling fn for each node or that children in the tree, including root.

# Constants

These variables are the Node types.
These variables are the Node types.
These variables are the Node types.
These variables are the Node types.
These variables are the Node types.
These variables are the Node types.
These variables are the Node types.
VERSION is the version number.

# Variables

EQ is `==`.
GE is `>=`.
GT is `>`.
LE is `<=`.
LT is `<`.
NE is `!=`.
No description provided by the author
RE is `~=`.
SkipWalk is used as a return value from WalkFunc to indicate that the node and that children in the call is to be skipped.

# Structs

Any is an interface that defines any node.
ColorEncoder writes JSON or YAML values with color to an output stream.
Comparator represents a comparable selector.
No description provided by the author
No description provided by the author
No description provided by the author
NopQuery is a query that implements no-op Exec method.
SelectQuery returns nodes that matched by selectors.
SlurpQuery is a special query that works in FilterQuery.
ValueQuery is a query that returns the constant value.
No description provided by the author

# Interfaces

EditorNode is an interface that defines the methods to edit this node.
EditorQuery is an interface that defines the methods to edit a node.
A Node is an element on the tree.
Query is an interface that defines the methods to query a node.
Selector checks if a node is eligible for selection.
Value provides the accessor of primitive value.

# Type aliases

And represents selectors that combines each selector with and.
Array represents an array of Node.
ArrayQuery is an index of the Array that implements methods of the Query.
ArrayRangeQuery represents a range of the Array that implements methods of the Query.
A BoolValue represents a bool value.
FilterQuery consists of multiple queries that filter the nodes in order.
Map represents a map of Node.
MapQuery is a key of the Map that implements methods of the Query.
A NumberValue represents an number value.
Operator represents an operator.
Or represents selectors that combines each selector with or.
A StringValue represents a string value.
Type represents the Node type.
WalkFunc is the type of the function called by Walk to visit each nodes.
WalkQuery is a key of each nodes that implements methods of the Query.