jsonquery

repository·master·Indexed 43 days ago

https://github.com/antchfx/jsonquery

A Go package for querying JSON documents using XPath syntax. It allows developers to extract specific data from JSON structures without defining Go structs for unmarshaling. Features include loading JSON from URLs, strings, or io.Readers, converting JSON to XML via OutputXML(), and navigating documents using the NodeNavigator and Node types.

Tokens
2.6K
Snippets
12
Records
14
Agent score
55%

What's inside jsonquery

  1. Load JSON documents

    master

    You can load JSON data into a jsonquery.Document using several methods depending on the source:

    • From a URL: Use jsonquery.LoadURL(url).
    • From a string: Use jsonquery.Parse(strings.NewReader(s)).
    • From an io.Reader: Use jsonquery.Parse(reader) (e.g., an *os.File).
    // Load from URL
    doc, err := jsonquery.LoadURL("http://www.example.com/feed?json")
    
    // Load from string
    s := `{"name":"John"}`
    doc, err := jsonquery.Parse(strings.NewReader(s))
    
    // Load from io.Reader
    f, err := os.Open("./books.json")
    doc, err := jsonquery.Parse(f)
  2. Understand NodeNavigator value handling

    master

    When using NodeNavigator, the Value() and GetValue() methods handle JSON data types differently:

    1. GetValue() interface{}: Returns the original Go type extracted from the JSON (e.g., string, float64, bool, map[string]interface{}, or []interface{}).
    2. Value() string:
      • If the node is a TextNode and the value is a string, it returns the string directly.
      • For other types (objects, arrays, numbers), it returns a JSON-marshaled string representation of the value. If marshaling fails, it falls back to fmt.Sprintf("%v", value).
    3. String() string: Implements the fmt.Stringer interface by calling Value().
  3. Iterate over JSON child nodes

    master

    You can iterate over the immediate children of a document or a specific node using the ChildNodes() method.

    doc, err := jsonquery.Parse(strings.NewReader(s))
    for _, n := range doc.ChildNodes() {
        fmt.Printf("%s: %v[%T]\n", n.Data, n.Value(), n.Value())
    }
  4. Extract data using XPath queries

    master

    Use XPath-like syntax to navigate and extract data from JSON without defining Go structs.

    Query Methods

    • FindOne(doc, xpath): Returns the first matching node. Note: This will panic if the XPath expression is invalid.
    • Find(doc, xpath): Returns a list of all matching nodes.
    • Query(doc, xpath): Returns the first matching node and an error. Use this instead of FindOne to handle invalid XPath expressions gracefully.
    • QueryAll(doc, xpath): Returns a list of all matching nodes and an error.
    • QuerySelector(doc, compiledXPath): Uses a pre-compiled xpath.NodeXPath.
    • QuerySelectorAll(doc, compiledXPath): Uses a pre-compiled xpath.NodeXPath for multiple matches.

    Accessing Values

    Once you have a node, call .Value() to get the underlying Go type.

    // Find a single value
    age := jsonquery.FindOne(doc, "person/age")
    fmt.Println(age.Value()) // e.g., 31
    
    // Find multiple values
    hobbies := jsonquery.Find(doc, "//hobbies/*")
    for _, n := range hobbies {
        fmt.Println(n.Value())
    }
  5. Navigate JSON documents with NodeNavigator

    master

    The NodeNavigator type implements the xpath.NodeNavigator interface, allowing you to traverse a JSON document as if it were an XML tree. It is used internally by the query engine but can be manually created using CreateXPathNavigator.

    Key methods for navigation:

    • Current() *Node: Returns the node currently being pointed to.
    • MoveToChild() bool: Moves to the first child of the current node. Returns false if no child exists.
    • MoveToNext() bool: Moves to the next sibling. Returns false if no next sibling exists.
    • MoveToPrevious() bool: Moves to the previous sibling. Returns false if no previous sibling exists.
    • MoveToParent() bool: Moves to the parent node. Returns false if no parent exists.
    • MoveToRoot(): Resets the navigator to the document root.
    • Value() string: Returns the string representation of the current node's value (JSON-marshaled if the value is a complex type).
    • GetValue() interface{}: Returns the raw underlying Go value of the node.
    // Create a navigator starting at a specific node
    nv := jsonquery.CreateXPathNavigator(topNode)
    
    // Traverse manually
    if nv.MoveToChild() {
        fmt.Println("Current node value:", nv.Value())
        fmt.Println("Raw value:", nv.GetValue())
    }
  6. Navigate and inspect JSON with the Node type

    master

    The Node type is the core building block of the jsonquery tree. Each node represents a part of the JSON structure (an object key, an array element, or a primitive value).

    Node Types

    • DocumentNode: The root of the document tree.
    • ElementNode: Represents a JSON object key or an array element.
    • TextNode: Represents a primitive value (string, number, boolean).

    Key Methods

    • Value() interface{}: Returns the underlying Go value (e.g., map[string]interface{}, []interface{}, or a primitive).
    • ChildNodes() []*Node: Returns a slice of all immediate child nodes.
    • SelectElement(name string) *Node: Searches the immediate children for the first node where Data matches the provided name. Returns nil if not found.
    • InnerText() string: Recursively collects and returns the text content of the node and all its descendants as a single string.
    // Example: Accessing a value and finding a child
    node, _ := jsonquery.Parse(reader)
    
    // Get the raw interface value
    val := node.Value()
    
    // Find a specific child element by its key name
    child := node.SelectElement("username")
    if child != nil {
        fmt.Println(child.InnerText())
    }
  7. Use Find and FindOne for panicking XPath queries

    master

    The Find and FindOne functions are convenience wrappers around QueryAll and Query. They are intended for use when the XPath expression is a constant and you want to simplify error handling by panicking if the expression is syntactically incorrect.

    • Find(top *Node, expr string) []*Node: Returns all matching nodes or panics if expr is invalid.
    • FindOne(top *Node, expr string) *Node: Returns the first matching node or panics if expr is invalid.
    // Panics if the expression is invalid
    firstUser := jsonquery.FindOne(root, "//user[0]")
    allUsers := jsonquery.Find(root, "//user")
  8. Query JSON using XPath expressions

    master

    You can search for specific nodes within a JSON structure using XPath expressions via Query and QueryAll.

    • Query(top *Node, expr string) (*Node, error): Returns the first node that matches the XPath expression. Returns an error if the expression is invalid.
    • QueryAll(top *Node, expr string) ([]*Node, error): Returns a slice of all nodes that match the XPath expression. Returns an error if the expression is invalid.

    If you are certain your XPath expression is valid and want to avoid manual error handling, you can use the helper functions FindOne and Find which will panic if the expression cannot be parsed.

    // Get the first matching node
    node, err := jsonquery.Query(rootNode, "//users/user[id='123']")
    if err != nil {
        // handle error
    }
    
    // Get all matching nodes
    nodes, err := jsonquery.QueryAll(rootNode, "//items/item")
    if err != nil {
        // handle error
    }
  9. Parse JSON documents with Parse() and LoadURL()

    master

    To work with JSON data in jsonquery, you must first parse it into a *Node tree. You can parse from an io.Reader (like a file or a string buffer) using Parse(), or fetch and parse directly from a URL using LoadURL().

    Both functions return a pointer to a Node representing the root of the document tree and an error if parsing fails.

    // From a reader
    node, err := jsonquery.Parse(reader)
    
    // From a URL
    node, err := jsonquery.LoadURL("https://api.example.com/data.json")