SkillAgentSearch skills...

Xmldot

High-performance Go library for querying and manipulating XML using GJSON-inspired path syntax. Zero dependencies, blazing fast, security-first design.

Install / Use

/learn @netascode/Xmldot
About this skill

Quality Score

0/100

Category

Design

Supported Platforms

Universal

README

<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset=".github/images/logo-dark.svg"> <source media="(prefers-color-scheme: light)" srcset=".github/images/logo.svg"> <img src=".github/images/logo.svg" alt="xmldot logo" width="120"> </picture> </p> <p align="center"> <a href="https://github.com/netascode/xmldot/tags"><img src="https://img.shields.io/github/v/tag/netascode/xmldot?style=flat-square" alt="Latest Version"></a> <br> <a href="https://godoc.org/github.com/netascode/xmldot"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a> <a href="https://goreportcard.com/report/github.com/netascode/xmldot"><img src="https://goreportcard.com/badge/github.com/netascode/xmldot?style=flat-square" alt="Go Report Card"></a> <a href="https://codecov.io/gh/netascode/xmldot"><img src="https://codecov.io/gh/netascode/xmldot/branch/main/graph/badge.svg?style=flat-square" alt="codecov"></a> <a href="https://netascode.github.io/xmldot-playground/"><img src="https://img.shields.io/badge/playground-try_online-green.svg?style=flat-square" alt="Playground"></a> </p> <h3 align="center">get and set xml values with dot notation</h3>

XMLDOT is a Go package that provides a fast and simple way to get and set values in XML documents. It has features such as dot notation paths, wildcards, filters, modifiers, and array operations.

Inspired by GJSON and SJSON for JSON.

Getting Started

Installing

To start using XMLDOT, install Go and run go get:

$ go get -u github.com/netascode/xmldot

This will retrieve the library.

Try It Online

🎮 Interactive Playground

Experiment with XMLDOT in your browser without installing anything. The playground lets you:

  • Test path queries against sample or custom XML
  • Explore filters, wildcards, and modifiers
  • See results in real-time
  • Learn the syntax interactively

Perfect for learning or prototyping queries before using in code.

Get a value

Get searches XML for the specified path. A path is in dot syntax, such as "book.title" or "book.@id". When the value is found it's returned immediately.

package main

import "github.com/netascode/xmldot"

const xml = `
<catalog>
    <book id="1">
        <title>The Go Programming Language</title>
        <author>Alan Donovan</author>
        <price>44.99</price>
    </book>
</catalog>`

func main() {
    title := xmldot.Get(xml, "catalog.book.title")
    println(title.String())
}

This will print:

The Go Programming Language

Fluent API

The fluent API enables method chaining on Result objects for cleaner, more readable code:

// Basic fluent chaining
root := xmldot.Get(xml, "root")
name := root.Get("user.name").String()
age := root.Get("user.age").Int()

// Deep chaining
fullPath := xmldot.Get(xml, "root").
    Get("company").
    Get("department").
    Get("team.member").
    Get("name").
    String()

// Batch queries
user := xmldot.Get(xml, "root.user")
results := user.GetMany("name", "age", "email")
name := results[0].String()
age := results[1].Int()
email := results[2].String()

// Case-insensitive queries
opts := &xmldot.Options{CaseSensitive: false}
name := root.GetWithOptions("USER.NAME", opts).String()

// Structure inspection with Map()
user := xmldot.Get(xml, "root.user")
m := user.Map()
name := m["name"].String()
age := m["age"].Int()

// Iterate over all child elements
for key, value := range m {
    fmt.Printf("%s = %s\n", key, value.String())
}

// Map() with case-insensitive keys
m := user.MapWithOptions(&xmldot.Options{CaseSensitive: false})

Performance: Fluent chaining adds ~280% overhead for 3-level chains compared to full paths. For performance-critical code, use direct paths:

// Fast (recommended for hot paths)
name := xmldot.Get(xml, "root.user.name")

// Readable (recommended for business logic)
user := xmldot.Get(xml, "root.user")
name := user.Get("name")

Array Handling: Field extraction on arrays requires explicit #.field syntax:

items := xmldot.Get(xml, "catalog.items")
// Extract all prices
prices := items.Get("item.#.price")  // Array of all prices

Set a value

Set modifies an XML value for the specified path. A path is in dot syntax, such as "book.title" or "book.@id".

package main

import "github.com/netascode/xmldot"

const xml = `
<catalog>
    <book id="1">
        <title>The Go Programming Language</title>
        <price>44.99</price>
    </book>
</catalog>`

func main() {
    value, _ := xmldot.Set(xml, "catalog.book.price", 39.99)
    println(value)
}

This will print:

<catalog><book id="1"><title>The Go Programming Language</title><price>39.99</price></book></catalog>

Creating Elements with Attributes

Set automatically creates missing parent elements when setting attributes. This makes it easy to add attributes to elements that don't yet exist:

xml := `<root></root>`

// Automatically creates <user> element with id attribute
result, _ := xmldot.Set(xml, "root.user.@id", "123")
// Result: <root><user id="123"></user></root>

// Works with deep paths too
result, _ = xmldot.Set(xml, "root.company.department.@name", "Engineering")
// Result: <root><company><department name="Engineering"></department></company></root>

Path Syntax

A path is a series of keys separated by a dot. The dot character can be escaped with \.

<catalog>
    <book id="1">
        <title>The Go Programming Language</title>
        <author>Alan Donovan</author>
        <price>44.99</price>
        <tags>
            <tag>programming</tag>
            <tag>go</tag>
        </tags>
    </book>
    <book id="2">
        <title>Learning Go</title>
        <author>Jon Bodner</author>
        <price>39.99</price>
    </book>
</catalog>
catalog.book.title           >> "The Go Programming Language"
catalog.book.@id             >> "1"
catalog.book.price           >> "44.99"
catalog.book.1.title         >> "Learning Go"
catalog.book.#               >> 2
catalog.book.tags.tag.0      >> "programming"
catalog.book.title.%         >> "The Go Programming Language"

Arrays

Array elements are accessed by index:

catalog.book.0.title         >> "The Go Programming Language" (first book)
catalog.book.1.title         >> "Learning Go" (second book)
catalog.book.#               >> 2 (count of books)
catalog.book.tags.tag.#      >> 2 (count of tags)

Append new elements using index -1 with Set() or SetRaw():

xml := `<catalog><book><title>Book 1</title></book></catalog>`

// Append a new book using SetRaw for XML content
result, _ := xmldot.SetRaw(xml, "catalog.book.-1", "<title>Book 2</title>")
count := xmldot.Get(result, "catalog.book.#")
// count.Int() → 2

// Works with empty arrays too
xml2 := `<catalog></catalog>`
result2, _ := xmldot.SetRaw(xml2, "catalog.book.-1", "<title>First Book</title>")
// Result: <catalog><book><title>First Book</title></book></catalog>

Attributes

Attributes are accessed with the @ prefix:

catalog.book.@id             >> "1"
catalog.book.0.@id           >> "1"
catalog.book.1.@id           >> "2"

Text Content

Text content (ignoring child elements) uses the % operator:

catalog.book.title.%         >> "The Go Programming Language"

Wildcards

Single-level wildcards * match any element at that level. Recursive wildcards ** match elements at any depth:

<catalog>
    <book id="1">
        <title>The Go Programming Language</title>
        <price>44.99</price>
    </book>
    <book id="2">
        <title>Learning Go</title>
        <price>39.99</price>
    </book>
</catalog>
catalog.*.title              >> ["The Go Programming Language", "Learning Go"]
catalog.book.*.%             >> ["The Go Programming Language", "Alan Donovan", "44.99", ...]
catalog.**.price             >> ["44.99", "39.99"] (all prices at any depth)

Filters

You can filter elements using GJSON-style query syntax. Supports ==, !=, <, >, <=, >=, %, !% operators:

<catalog>
    <book status="active">
        <title>The Go Programming Language</title>
        <price>44.99</price>
    </book>
    <book status="active">
        <title>Learning Go</title>
        <price>39.99</price>
    </book>
    <book status="discontinued">
        <title>Old Book</title>
        <price>19.99</price>
    </book>
</catalog>
catalog.book.#(price>40).title               >> "The Go Programming Language"
catalog.book.#(@status==active)#.title       >> ["The Go...", "Learning Go"]
catalog.book.#(price<30).#(@status==active)  >> [] (no matches)
catalog.book.#(title%"*Go*")#.title          >> ["The Go...", "Learning Go"] (pattern match)

Modifiers

Modifiers transform query results using the | operator:

catalog.book.title|@reverse                  >> ["Learning Go", "The Go..."]
catalog.book.price|@sort                     >> ["39.99", "44.99"]
catalog.book.title|@first                    >> "The Go Programming Language"
catalog.book.title|@last                     >> "Learning Go"
catalog.book|@pretty                         >> formatted XML

Built-in modifiers

  • @reverse: Reverse array order
  • @sort: Sort array elements
  • @first: Get first element
  • @last: Get last element
  • @keys: Get element names
  • @values: Get element values
  • @flatten: Flatten nested arrays
  • @pretty: Format XML with indentation
  • @ugly: Remove all whitespace
  • @raw: Get raw XML without parsing

Custom modifiers

You can add your own modifiers:

xmldot.AddModifier("uppercase", func(xml, arg string) string {
    return strings.ToUpper(xml)
})

result := xmldot.Get(xml, "c

Related Skills

View on GitHub
GitHub Stars20
CategoryDesign
Updated3d ago
Forks0

Languages

Go

Security Score

95/100

Audited on Apr 4, 2026

No findings