-
Notifications
You must be signed in to change notification settings - Fork 209
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from pelletier/jsonpath
Powerful querying interface
- Loading branch information
Showing
18 changed files
with
2,412 additions
and
251 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
// Package toml is a TOML markup language parser. | ||
// | ||
// This version supports the specification as described in | ||
// https://github.com/toml-lang/toml/blob/master/versions/toml-v0.2.0.md | ||
// | ||
// TOML Parsing | ||
// | ||
// TOML data may be parsed in two ways: by file, or by string. | ||
// | ||
// // load TOML data by filename | ||
// tree, err := toml.LoadFile("filename.toml") | ||
// | ||
// // load TOML data stored in a string | ||
// tree, err := toml.Load(stringContainingTomlData) | ||
// | ||
// Either way, the result is a TomlTree object that can be used to navigate the | ||
// structure and data within the original document. | ||
// | ||
// | ||
// Getting data from the TomlTree | ||
// | ||
// After parsing TOML data with Load() or LoadFile(), use the Has() and Get() | ||
// methods on the returned TomlTree, to find your way through the document data. | ||
// | ||
// if tree.Has('foo') { | ||
// fmt.Prinln("foo is: %v", tree.Get('foo')) | ||
// } | ||
// | ||
// Working with Paths | ||
// | ||
// Go-toml has support for basic dot-separated key paths on the Has(), Get(), Set() | ||
// and GetDefault() methods. These are the same kind of key paths used within the | ||
// TOML specification for struct tames. | ||
// | ||
// // looks for a key named 'baz', within struct 'bar', within struct 'foo' | ||
// tree.Has("foo.bar.baz") | ||
// | ||
// // returns the key at this path, if it is there | ||
// tree.Get("foo.bar.baz") | ||
// | ||
// TOML allows keys to contain '.', which can cause this syntax to be problematic | ||
// for some documents. In such cases, use the GetPath(), HasPath(), and SetPath(), | ||
// methods to explicitly define the path. This form is also faster, since | ||
// it avoids having to parse the passed key for '.' delimiters. | ||
// | ||
// // looks for a key named 'baz', within struct 'bar', within struct 'foo' | ||
// tree.HasPath(string{}{"foo","bar","baz"}) | ||
// | ||
// // returns the key at this path, if it is there | ||
// tree.GetPath(string{}{"foo","bar","baz"}) | ||
// | ||
// Note that this is distinct from the heavyweight query syntax supported by | ||
// TomlTree.Query() and the Query() struct (see below). | ||
// | ||
// Position Support | ||
// | ||
// Each element within the TomlTree is stored with position metadata, which is | ||
// invaluable for providing semantic feedback to a user. This helps in | ||
// situations where the TOML file parses correctly, but contains data that is | ||
// not correct for the application. In such cases, an error message can be | ||
// generated that indicates the problem line and column number in the source | ||
// TOML document. | ||
// | ||
// // load TOML data | ||
// tree, _ := toml.Load("filename.toml") | ||
// | ||
// // get an entry and report an error if it's the wrong type | ||
// element := tree.Get("foo") | ||
// if value, ok := element.(int64); !ok { | ||
// return fmt.Errorf("%v: Element 'foo' must be an integer", tree.GetPosition("foo")) | ||
// } | ||
// | ||
// // report an error if an expected element is missing | ||
// if !tree.Has("bar") { | ||
// return fmt.Errorf("%v: Expected 'bar' element", tree.GetPosition("")) | ||
// } | ||
// | ||
// Query Support | ||
// | ||
// The TOML query path implementation is based loosely on the JSONPath specification: | ||
// http://goessner.net/articles/JsonPath/ | ||
// | ||
// The idea behind a query path is to allow quick access to any element, or set | ||
// of elements within TOML document, with a single expression. | ||
// | ||
// result := tree.Query("$.foo.bar.baz") // result is 'nil' if the path is not present | ||
// | ||
// This is equivalent to: | ||
// | ||
// next := tree.Get("foo") | ||
// if next != nil { | ||
// next = next.Get("bar") | ||
// if next != nil { | ||
// next = next.Get("baz") | ||
// } | ||
// } | ||
// result := next | ||
// | ||
// As illustrated above, the query path is much more efficient, especially since | ||
// the structure of the TOML file can vary. Rather than making assumptions about | ||
// a document's structure, a query allows the programmer to make structured | ||
// requests into the document, and get zero or more values as a result. | ||
// | ||
// The syntax of a query begins with a root token, followed by any number | ||
// sub-expressions: | ||
// | ||
// $ | ||
// Root of the TOML tree. This must always come first. | ||
// .name | ||
// Selects child of this node, where 'name' is a TOML key | ||
// name. | ||
// ['name'] | ||
// Selects child of this node, where 'name' is a string | ||
// containing a TOML key name. | ||
// [index] | ||
// Selcts child array element at 'index'. | ||
// ..expr | ||
// Recursively selects all children, filtered by an a union, | ||
// index, or slice expression. | ||
// ..* | ||
// Recursive selection of all nodes at this point in the | ||
// tree. | ||
// .* | ||
// Selects all children of the current node. | ||
// [expr,expr] | ||
// Union operator - a logical 'or' grouping of two or more | ||
// sub-expressions: index, key name, or filter. | ||
// [start:end:step] | ||
// Slice operator - selects array elements from start to | ||
// end-1, at the given step. All three arguments are | ||
// optional. | ||
// [?(filter)] | ||
// Named filter expression - the function 'filter' is | ||
// used to filter children at this node. | ||
// | ||
// Query Indexes And Slices | ||
// | ||
// Index expressions perform no bounds checking, and will contribute no | ||
// values to the result set if the provided index or index range is invalid. | ||
// Negative indexes represent values from the end of the array, counting backwards. | ||
// | ||
// // select the last index of the array named 'foo' | ||
// tree.Query("$.foo[-1]") | ||
// | ||
// Slice expressions are supported, by using ':' to separate a start/end index pair. | ||
// | ||
// // select up to the first five elements in the array | ||
// tree.Query("$.foo[0:5]") | ||
// | ||
// Slice expressions also allow negative indexes for the start and stop | ||
// arguments. | ||
// | ||
// // select all array elements. | ||
// tree.Query("$.foo[0:-1]") | ||
// | ||
// Slice expressions may have an optional stride/step parameter: | ||
// | ||
// // select every other element | ||
// tree.Query("$.foo[0:-1:2]") | ||
// | ||
// Slice start and end parameters are also optional: | ||
// | ||
// // these are all equivalent and select all the values in the array | ||
// tree.Query("$.foo[:]") | ||
// tree.Query("$.foo[0:]") | ||
// tree.Query("$.foo[:-1]") | ||
// tree.Query("$.foo[0:-1:]") | ||
// tree.Query("$.foo[::1]") | ||
// tree.Query("$.foo[0::1]") | ||
// tree.Query("$.foo[:-1:1]") | ||
// tree.Query("$.foo[0:-1:1]") | ||
// | ||
// Query Filters | ||
// | ||
// Query filters are used within a Union [,] or single Filter [] expression. | ||
// A filter only allows nodes that qualify through to the next expression, | ||
// and/or into the result set. | ||
// | ||
// // returns children of foo that are permitted by the 'bar' filter. | ||
// tree.Query("$.foo[?(bar)]") | ||
// | ||
// There are several filters provided with the library: | ||
// | ||
// tree | ||
// Allows nodes of type TomlTree. | ||
// int | ||
// Allows nodes of type int64. | ||
// float | ||
// Allows nodes of type float64. | ||
// string | ||
// Allows nodes of type string. | ||
// time | ||
// Allows nodes of type time.Time. | ||
// bool | ||
// Allows nodes of type bool. | ||
// | ||
// Query Results | ||
// | ||
// An executed query returns a QueryResult object. This contains the nodes | ||
// in the TOML tree that qualify the query expression. Position information | ||
// is also available for each value in the set. | ||
// | ||
// // display the results of a query | ||
// results := tree.Query("$.foo.bar.baz") | ||
// for idx, value := results.Values() { | ||
// fmt.Println("%v: %v", results.Positions()[idx], value) | ||
// } | ||
// | ||
// Compiled Queries | ||
// | ||
// Queries may be executed directly on a TomlTree object, or compiled ahead | ||
// of time and executed discretely. The former is more convienent, but has the | ||
// penalty of having to recompile the query expression each time. | ||
// | ||
// // basic query | ||
// results := tree.Query("$.foo.bar.baz") | ||
// | ||
// // compiled query | ||
// query := toml.CompileQuery("$.foo.bar.baz") | ||
// results := query.Execute(tree) | ||
// | ||
// // run the compiled query again on a different tree | ||
// moreResults := query.Execute(anotherTree) | ||
// | ||
// User Defined Query Filters | ||
// | ||
// Filter expressions may also be user defined by using the SetFilter() | ||
// function on the Query object. The function must return true/false, which | ||
// signifies if the passed node is kept or discarded, respectively. | ||
// | ||
// // create a query that references a user-defined filter | ||
// query, _ := CompileQuery("$[?(bazOnly)]") | ||
// | ||
// // define the filter, and assign it to the query | ||
// query.SetFilter("bazOnly", func(node interface{}) bool{ | ||
// if tree, ok := node.(*TomlTree); ok { | ||
// return tree.Has("baz") | ||
// } | ||
// return false // reject all other node types | ||
// }) | ||
// | ||
// // run the query | ||
// query.Execute(tree) | ||
// | ||
package toml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// code examples for godoc | ||
|
||
package toml | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
func ExampleNodeFilterFn_filterExample() { | ||
tree, _ := Load(` | ||
[struct_one] | ||
foo = "foo" | ||
bar = "bar" | ||
[struct_two] | ||
baz = "baz" | ||
gorf = "gorf" | ||
`) | ||
|
||
// create a query that references a user-defined-filter | ||
query, _ := CompileQuery("$[?(bazOnly)]") | ||
|
||
// define the filter, and assign it to the query | ||
query.SetFilter("bazOnly", func(node interface{}) bool { | ||
if tree, ok := node.(*TomlTree); ok { | ||
return tree.Has("baz") | ||
} | ||
return false // reject all other node types | ||
}) | ||
|
||
// results contain only the 'struct_two' TomlTree | ||
query.Execute(tree) | ||
} | ||
|
||
func ExampleQuery_queryExample() { | ||
config, _ := Load(` | ||
[[book]] | ||
title = "The Stand" | ||
author = "Stephen King" | ||
[[book]] | ||
title = "For Whom the Bell Tolls" | ||
author = "Ernest Hemmingway" | ||
[[book]] | ||
title = "Neuromancer" | ||
author = "William Gibson" | ||
`) | ||
|
||
// find and print all the authors in the document | ||
authors, _ := config.Query("$.book.author") | ||
for _, name := range authors.Values() { | ||
fmt.Println(name) | ||
} | ||
} | ||
|
||
func Example_comprehensiveExample() { | ||
config, err := LoadFile("config.toml") | ||
|
||
if err != nil { | ||
fmt.Println("Error ", err.Error()) | ||
} else { | ||
// retrieve data directly | ||
user := config.Get("postgres.user").(string) | ||
password := config.Get("postgres.password").(string) | ||
|
||
// or using an intermediate object | ||
configTree := config.Get("postgres").(*TomlTree) | ||
user = configTree.Get("user").(string) | ||
password = configTree.Get("password").(string) | ||
fmt.Println("User is ", user, ". Password is ", password) | ||
|
||
// show where elements are in the file | ||
fmt.Println("User position: %v", configTree.GetPosition("user")) | ||
fmt.Println("Password position: %v", configTree.GetPosition("password")) | ||
|
||
// use a query to gather elements without walking the tree | ||
results, _ := config.Query("$..[user,password]") | ||
for ii, item := range results.Values() { | ||
fmt.Println("Query result %d: %v", ii, item) | ||
} | ||
} | ||
} |
Oops, something went wrong.