Skip to content

Read XML

The core reads and writes only YAML. XML comes from a sibling module, config-xml, which adds nothing to your dependency graph — it reads XML with the standard library.

go get gitlab.com/phpboyscout/go/config-xml
import (
    "gitlab.com/phpboyscout/go/config"
    configxml "gitlab.com/phpboyscout/go/config-xml"
)

store, err := config.NewStore(ctx,
    config.WithFiles(fsys, "/etc/app.yaml"),                    // YAML, the default
    config.WithBackend(configxml.New(fsys, "/etc/log4j.xml")),  // outranks it
)

When you do not need this

XML here is read-only, so it cannot be the layer your application writes back to. If you own the format, YAML or JSON will serve you better in every respect.

Reach for XML when the file is not yours — an existing enterprise or legacy document you have to consume where it is, rather than convert and keep in step.

Attributes and elements share one namespace

Real configuration XML is attribute-heavy — log4j and .NET appSettings are almost entirely attributes — so an attribute reads by the same dotted path a child element would:

<server port="8080"><host>localhost</host></server>
port := store.View().GetInt("server.port")     // 8080  (an attribute)
host := store.View().GetString("server.host")   // "localhost"  (a child element)

A repeated element becomes a slice, and a single one a scalar that the read path promotes to a one-element slice when asked — so declaring a repeatable element as a slice reads correctly whether the file has one entry or many:

aliases := store.View().GetStringSlice("alias") // one <alias> or several, both decode

What it refuses, at load

Two shapes cannot be a value tree, and are refused with config.ErrBackendUnsafe naming the element rather than silently dropping half of it:

  • an attribute and a child element that share a name — two candidates for one key, and no basis to choose between them;
  • an element with both text content and attributes or children — it is at once a scalar and a map, and a key cannot be both.

This is the same treatment an ambiguous environment variable name gets: two candidates, no basis to choose, so it says so rather than picking one.

Read-only

No Go library round-trips XML preserving formatting and comments, so config-xml reads and does not write. A write to a key an XML layer defines is routed past the read-only layer and reported shadowed rather than failing.

What it costs

Modules added none — everything it links comes from the config graph, and an allowlist test in the module fails if that changes

encoding/xml is standard library, so there is no parser module — and no filesystem library either, because you supply the config.FS.