{"id":25722402,"url":"https://github.com/jarxorg/tree","last_synced_at":"2026-03-11T16:39:24.063Z","repository":{"id":42006226,"uuid":"438926389","full_name":"jarxorg/tree","owner":"jarxorg","description":"Simple tree structure and a handy command line tool named 'tq'","archived":false,"fork":false,"pushed_at":"2024-03-22T03:14:02.000Z","size":152,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-31T04:12:23.615Z","etag":null,"topics":["dynamic","go","json","tq","tree","yaml"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jarxorg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-16T09:09:07.000Z","updated_at":"2024-09-18T12:58:55.000Z","dependencies_parsed_at":"2024-03-22T03:47:36.155Z","dependency_job_id":null,"html_url":"https://github.com/jarxorg/tree","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarxorg%2Ftree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarxorg%2Ftree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarxorg%2Ftree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarxorg%2Ftree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jarxorg","download_url":"https://codeload.github.com/jarxorg/tree/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252788514,"owners_count":21804284,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dynamic","go","json","tq","tree","yaml"],"created_at":"2025-02-25T19:28:13.755Z","updated_at":"2026-03-11T16:39:24.057Z","avatar_url":"https://github.com/jarxorg.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tree\n\n[![PkgGoDev](https://pkg.go.dev/badge/github.com/jarxorg/tree)](https://pkg.go.dev/github.com/jarxorg/tree)\n[![Report Card](https://goreportcard.com/badge/github.com/jarxorg/tree)](https://goreportcard.com/report/github.com/jarxorg/tree)\n\nTree is a simple structure for dealing with dynamic or unknown JSON/YAML in Go.\n\n## Table of Contents\n\n- [Features](#features)\n- [Road to 1.0](#road-to-10)\n- [Syntax](#syntax)\n- [Marshal and Unmarshal](#marshal-and-unmarshal)\n- [Get](#get)\n- [Find](#find)\n  - [Query](#query)\n  - [Built-in Methods](#built-in-methods)\n- [Edit](#edit)\n- [tq](#tq)\n- [Third-party library licenses](#third-party-library-licenses)\n\n## Features\n\n- Parses json/yaml of unknown structure to get to nodes with fluent interface.\n- Syntax similar to Go standard and map and slice.\n- Find function can be specified the [Query](#query) expression with [built-in methods](#built-in-methods).\n- Edit function can be specified the [Edit](#edit) expression.\n- Bundled 'tq' that is a portable command-line JSON/YAML processor.\n\n## Road to 1.0\n\n- Placeholders in query.\n- Merge support in tq.\n\n## Syntax\n\n### Go\n\n```go\ntree.Map{\n\t\"ID\":     tree.ToValue(1),\n\t\"Name\":   tree.ToValue(\"Reds\"),\n\t\"Colors\": tree.ToArrayValues(\"Crimson\", \"Red\", \"Ruby\", \"Maroon\"),\n}\n```\n\n### JSON\n\n```json\n{\n\t\"ID\": 1,\n\t\"Name\": \"Reds\",\n\t\"Colors\": [\"Crimson\", \"Red\", \"Ruby\", \"Maroon\"]\n}\n```\n\n### YAML\n\n```yaml\nID: 1\nName: Reds\nColors:\n- Crimson\n- Red\n- Ruby\n- Maroon\n```\n\n## Marshal and Unmarshal\n\n```go\nfunc ExampleMarshalJSON() {\n\tgroup := tree.Map{\n\t\t\"ID\":     tree.ToValue(1),\n\t\t\"Name\":   tree.ToValue(\"Reds\"),\n\t\t\"Colors\": tree.ToArrayValues(\"Crimson\", \"Red\", \"Ruby\", \"Maroon\"),\n\t}\n\tb, err := json.Marshal(group)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Println(string(b))\n\n\t// Output:\n\t// {\"Colors\":[\"Crimson\",\"Red\",\"Ruby\",\"Maroon\"],\"ID\":1,\"Name\":\"Reds\"}\n}\n```\n\n```go\nfunc ExampleUnmarshalJSON() {\n\tdata := []byte(`[\n  {\"Name\": \"Platypus\", \"Order\": \"Monotremata\"},\n  {\"Name\": \"Quoll\",    \"Order\": \"Dasyuromorphia\"}\n]`)\n\n\tvar animals tree.Array\n\terr := json.Unmarshal(data, \u0026animals)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Printf(\"%+v\\n\", animals)\n\n\t// Output:\n\t// [map[Name:Platypus Order:Monotremata] map[Name:Quoll Order:Dasyuromorphia]]\n}\n```\n\n### Using other parsers\n\nTree may work on other parsers that are compatible with \"encoding/json\" or \"gopkg.in/yaml.v2\". See [examples](examples) directory.\n\n### Alternate json.RawMessage\n\nFor example, [Dynamic JSON in Go](https://eagain.net/articles/go-dynamic-json/) shows an example of using json.RawMessage.\n\nIt may be simpler to use tree.Map instead of json.RawMessage.\n\n```go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/jarxorg/tree\"\n)\n\nconst input = `\n{\n\t\"type\": \"sound\",\n\t\"msg\": {\n\t\t\"description\": \"dynamite\",\n\t\t\"authority\": \"the Bruce Dickinson\"\n\t}\n}\n`\n\ntype Envelope struct {\n\tType string\n\tMsg  tree.Map\n}\n\nfunc main() {\n\tenv := Envelope{}\n\tif err := json.Unmarshal([]byte(input), \u0026env); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Printf(\"%#v\\n\", env)\n\tfmt.Printf(\"%#v\\n\", env.Msg.Get(\"description\"))\n\n\t// Output:\n\t// main.Envelope{Type:\"sound\", Msg:tree.Map{\"authority\":\"the Bruce Dickinson\", \"description\":\"dynamite\"}}\n\t// \"dynamite\"\n}\n```\n\n## Get\n\n```go\nfunc ExampleGet() {\n\tgroup := tree.Map{\n\t\t\"ID\":     tree.ToValue(1),\n\t\t\"Name\":   tree.ToValue(\"Reds\"),\n\t\t\"Colors\": tree.ToArrayValues(\"Crimson\", \"Red\", \"Ruby\", \"Maroon\"),\n\t\t\"Nil\":    nil,\n\t}\n\tfmt.Println(group.Get(\"Colors\").Get(1))\n\tfmt.Println(group.Get(\"Colors\", 2))\n\tfmt.Println(group.Get(\"Colors\").Get(5).IsNil())\n\tfmt.Println(group.Get(\"Nil\").IsNil())\n\n\t// Output:\n\t// Red\n\t// Ruby\n\t// true\n\t// true\n}\n```\n\n## Find\n\n```go\nfunc ExampleFind() {\n\tgroup := tree.Map{\n\t\t\"ID\":     tree.ToValue(1),\n\t\t\"Name\":   tree.ToValue(\"Reds\"),\n\t\t\"Colors\": tree.ToArrayValues(\"Crimson\", \"Red\", \"Ruby\", \"Maroon\"),\n\t}\n\n\trs, err := group.Find(\".Colors[1:3]\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfor _, r := range rs {\n\t\tfmt.Println(r)\n\t}\n\n\t// Output:\n\t// Red\n\t// Ruby\n}\n```\n\n### Query\n\nFor more details on built-in methods, see the [Built-in Methods](#built-in-methods) section.\n\n| Query | Description | Results |\n| - | - | - |\n| .store.book[0] | The first book | {\"category\": \"reference\", \"author\": \"Nigel Rees\", \"title\": \"Sayings of the Century\", \"price\": 8.95, \"tags\": [...]} |\n| .store.book[0].price | The price of the first book | 8.95 |\n| .store.book.0.price | The price of the first book (using dot) | 8.95 |\n| .store.book[:2].price | All prices of books[0:2] (index 2 is exclusive) | 8.95, 12.99 |\n| .store.book[].author | All authors of all books | \"Nigel Rees\", \"Evelyn Waugh\", \"Herman Melville\", \"J. R. R. Tolkien\" |\n| ..author | All authors |  \"Nigel Rees\", \"Evelyn Waugh\", \"Herman Melville\", \"J. R. R. Tolkien\" |\n| ..author \\| [0] | The first author | \"Nigel Rees\" |\n| .store.book[.tags[.name == \"genre\" and .value == \"fiction\"]].title | All titles of books tagged \"fiction\" | \"Sword of Honour\", \"Moby Dick\" |\n| .store.book[(.category == \"fiction\" or .category == \"reference\") and .price \u003c 10].title | All titles of books that are categorized as \"fiction\" or \"reference\" and price \u003c 10 | \"Sayings of the Century\", \"Moby Dick\" |\n| .store.book[.title ~= \"^S\"].title | Titles beginning with \"S\" | \"Sayings of the Century\", \"Sword of Honour\" |\n| .store.book.count() | Count books | 4 |\n| .store.book[0].keys() | Sorted keys of the first book | [\"author\", \"category\", \"price\", \"tags\", \"title\"] |\n| .store.book[0].values() | Values of the first book | [\"Nigel Rees\", \"reference\", 8.95, [tag objects], \"Sayings of the Century\"] |\n| .store.book.last().has(\"isbn\") | Check if last book has ISBN | true |\n| .store.book[.author.contains(\"Tolkien\")] | Check if any author contains \"Tolkien\" | [{\"author\": \"J. R. R. Tolkien\", ...}] |\n| .store.book[0].category.type() | Get type of category field | \"string\" |\n| .store.book[0].empty() | Check if first book is empty | false |\n| .store.book.first().title | Get title of first book | \"Sayings of the Century\" |\n| .store.book.last().author | Get author of last book | \"J. R. R. Tolkien\" |\n\n#### Illustrative Object\n\n```json\n{\n  \"store\": {\n    \"bicycle\": {\n      \"color\": \"red\",\n      \"price\": 19.95\n    },\n    \"book\": [\n      {\n        \"author\": \"Nigel Rees\",\n        \"category\": \"reference\",\n        \"price\": 8.95,\n        \"title\": \"Sayings of the Century\",\n        \"tags\": [\n          { \"name\": \"genre\", \"value\": \"reference\" },\n          { \"name\": \"era\", \"value\": \"20th century\" },\n          { \"name\": \"theme\", \"value\": \"quotations\" }\n        ]\n      },\n      {\n        \"author\": \"Evelyn Waugh\",\n        \"category\": \"fiction\",\n        \"price\": 12.99,\n        \"title\": \"Sword of Honour\",\n        \"tags\": [\n          { \"name\": \"genre\", \"value\": \"fiction\" },\n          { \"name\": \"era\", \"value\": \"20th century\" },\n          { \"name\": \"theme\", \"value\": \"WWII\" }\n        ]\n      },\n      {\n        \"author\": \"Herman Melville\",\n        \"category\": \"fiction\",\n        \"isbn\": \"0-553-21311-3\",\n        \"price\": 8.99,\n        \"title\": \"Moby Dick\",\n        \"tags\": [\n          { \"name\": \"genre\", \"value\": \"fiction\" },\n          { \"name\": \"era\", \"value\": \"19th century\" },\n          { \"name\": \"theme\", \"value\": \"whale hunting\" }\n        ]\n      },\n      {\n        \"author\": \"J. R. R. Tolkien\",\n        \"category\": \"fiction\",\n        \"isbn\": \"0-395-19395-8\",\n        \"price\": 22.99,\n        \"title\": \"The Lord of the Rings\",\n        \"tags\": [\n          { \"name\": \"genre\", \"value\": \"fantasy\" },\n          { \"name\": \"era\", \"value\": \"20th century\" },\n          { \"name\": \"theme\", \"value\": \"good vs evil\" }\n        ]\n      }\n    ]\n  }\n}\n```\n\n### Built-in Methods\n\nTree provides several built-in methods for data manipulation and querying:\n\n#### Aggregate Methods\n- **`count()`** - Returns the count of elements in arrays or maps\n- **`keys()`** - Returns the keys of arrays (as indices) or maps\n- **`values()`** - Returns the values of arrays or maps as an array\n\n#### Condition Methods\n- **`empty()`** - Checks if the node is empty (empty arrays, maps, null values, or empty strings)\n- **`has(key)`** - Checks if the node has the specified key (works with arrays and maps)\n- **`contains(value)`** - Checks if the node contains the specified value (arrays, maps, or substring in strings)\n\n#### Data Type Methods\n- **`type()`** - Returns the type name of the node (\"array\", \"object\", \"string\", \"number\", \"boolean\", \"null\")\n\n#### Array Methods\n- **`first()`** - Returns the first element of an array\n- **`last()`** - Returns the last element of an array\n\n#### Examples\n\n```go\n// Count elements\nnode.Find(\".store.book.count()\")  // Returns: 4\n\n// Check if key exists\nnode.Find(\".store.book[0].has(\\\"title\\\")\")  // Returns: true\n\n// Check if contains value in string\nnode.Find(\".store.book[0].author.contains(\\\"Nigel\\\")\")  // Returns: true\n\n// Get type\nnode.Find(\".store.book[0].price.type()\")  // Returns: \"number\"\n\n// Array operations\nnode.Find(\".store.book.first().title\")  // Returns: \"Sayings of the Century\"\nnode.Find(\".store.book.last().title\")   // Returns: \"The Lord of the Rings\"\n```\n\n\n## Edit\n\n```go\nfunc ExampleEdit() {\n\tvar group tree.Node = tree.Map{\n\t\t\"ID\":     tree.ToValue(1),\n\t\t\"Name\":   tree.ToValue(\"Reds\"),\n\t\t\"Colors\": tree.ToArrayValues(\"Crimson\", \"Red\", \"Ruby\", \"Maroon\"),\n\t}\n\n\tif err := tree.Edit(\u0026group, \".Colors += \\\"Pink\\\"\"); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Printf(\"Append Pink to Colors:\\n  %+v\\n\", group)\n\n\tif err := tree.Edit(\u0026group, \".Name = \\\"Blue\\\"\"); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Printf(\"Set Blue to Name:\\n  %+v\\n\", group)\n\n\tif err := tree.Edit(\u0026group, \".Colors ^?\"); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Printf(\"Delete Colors:\\n  %+v\\n\", group)\n\n\t// Output:\n\t// Append Pink to Colors:\n\t//   map[Colors:[Crimson Red Ruby Maroon Pink] ID:1 Name:Reds]\n\t// Set Blue to Name:\n\t//   map[Colors:[Crimson Red Ruby Maroon Pink] ID:1 Name:Blue]\n\t// Delete Colors:\n\t//   map[ID:1 Name:Blue]\n}\n```\n\n## tq\n\ntq is a portable command-line JSON/YAML processor.\n\n### Installation\n\n```sh\ngo install github.com/jarxorg/tree/cmd/tq@latest\n```\n\nUsing Homebrew\n\n```sh\nbrew tap jarxorg/tree\nbrew install jarxorg/tree/tq\n```\n\nDownload binary\n\n```sh\n# For macOS (Darwin)\nVERSION=0.8.4 GOOS=Darwin GOARCH=arm64; curl -fsSL \"https://github.com/jarxorg/tree/releases/download/v${VERSION}/tree_${VERSION}_${GOOS}_${GOARCH}.tar.gz\" | tar xz tq \u0026\u0026 mv tq /usr/local/bin\n\n# For Linux x64\nVERSION=0.8.4 GOOS=Linux GOARCH=amd64; curl -fsSL \"https://github.com/jarxorg/tree/releases/download/v${VERSION}/tree_${VERSION}_${GOOS}_${GOARCH}.tar.gz\" | tar xz tq \u0026\u0026 mv tq /usr/local/bin\n\n# For Windows x64\nVERSION=0.8.4; curl -fsSL \"https://github.com/jarxorg/tree/releases/download/v${VERSION}/tree_${VERSION}_windows_amd64.zip\" -o tq.zip \u0026\u0026 unzip tq.zip tq.exe\n```\n\n### Usage\n\n```sh\ntq is a command-line JSON/YAML processor.\n\nUsage:\n  tq [flags] [query] ([file...])\n\nFlags:\n  -c, --color                  output with colors\n  -e, --edit stringArray       edit expression\n  -x, --expand                 expand results\n  -h, --help                   help for tq\n  -U, --inplace                update files, inplace\n  -i, --input-format string    input format (json or yaml)\n  -j, --input-json             alias --input-format json\n  -y, --input-yaml             alias --input-format yaml\n  -O, --output string          output file\n  -o, --output-format string   output format (json or yaml, default json)\n  -J, --output-json            alias --output-format json\n  -Y, --output-yaml            alias --output-format yaml\n  -r, --raw                    output raw strings\n  -s, --slurp                  slurp all results into an array\n  -t, --template string        golang text/template string\n  -v, --version                print version\n\nExamples:\n  % echo '{\"colors\": [\"red\", \"green\", \"blue\"]}' | tq '.colors[0]'\n  \"red\"\n\n  % echo '{\"users\":[{\"id\":1,\"name\":\"one\"},{\"id\":2,\"name\":\"two\"}]}' | tq -x -t '{{.id}}: {{.name}}' '.users'\n  1: one\n  2: two\n\n  % echo '{}' | tq -e '.colors = [\"red\", \"green\"]' -e '.colors += \"blue\"' .\n  {\n    \"colors\": [\n      \"red\",\n      \"green\",\n      \"blue\"\n    ]\n  }\n\n  # Using built-in methods\n  % echo '{\"books\": [{\"author\": \"Tolkien\"}, {\"author\": \"Hemingway\"}]}' | tq '.books[.author.contains(\"Tol\")]'\n  [{\"author\": \"Tolkien\"}]\n\n```\n\n### for jq user\n\n| tq | jq |\n| - | - |\n| tq '.store.book[0]' | jq '.store.book[0]' |\n| tq '.store.book[]' | jq '.store.book[]' |\n| tq '.store.book[:2].price' | jq '.store.book[:2][] \\| .price' |\n| tq '.store.book[.category == \"fiction\" and .price \u003c 10].title' | jq '.store.book[] \\| select(.category == \"fiction\" and .price \u003c 10) \\| .title' |\n\n\n## Third-party library licenses\n\n- [spf13/pflag](https://github.com/spf13/pflag/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarxorg%2Ftree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjarxorg%2Ftree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarxorg%2Ftree/lists"}