Mxj
Decode / encode XML to/from map[string]interface{} (or JSON); extract values with dot-notation paths and wildcards. Replaces x2j and j2x packages.
Install / Use
/learn @clbanning/MxjREADME
mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.
<h4>Installation</h4> Using go.mod: <pre> go get github.com/clbanning/mxj/v2@v2.7 </pre> <pre> import "github.com/clbanning/mxj/v2" </pre>... or just vendor the package.
<h4>Related Packages</h4>https://github.com/clbanning/checkxml provides functions for validating XML data.
<h4>Refactor Encoder - 2020.05.01</h4> Issue #70 highlighted that encoding large maps does not scale well, since the original logic used string appends operations. Using bytes.Buffer results in linear scaling for very large XML docs. (Metrics based on MacBook Pro i7 w/ 16 GB.)Nodes m.XML() time
54809 12.53708ms
109780 32.403183ms
164678 59.826412ms
482598 109.358007ms
<h4>Refactor Decoder - 2015.11.15</h4>
For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant. I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi. Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value. As shown by:
BenchmarkNewMapXml-4 100000 18043 ns/op
BenchmarkNewStructXml-4 100000 14892 ns/op
BenchmarkNewMapJson-4 300000 4633 ns/op
BenchmarkNewStructJson-4 300000 3427 ns/op
BenchmarkNewMapXmlBooks-4 20000 82850 ns/op
BenchmarkNewStructXmlBooks-4 20000 67822 ns/op
BenchmarkNewMapJsonBooks-4 100000 17222 ns/op
BenchmarkNewStructJsonBooks-4 100000 15309 ns/op
<h4>Notices</h4>
2025.08.10: mxj package no longer actively supported.
2022.11.28: v2.7 - add SetGlobalKeyMapPrefix to change default prefix, '#', for default keys
2022.11.20: v2.6 - add NewMapForattedXmlSeq for XML docs formatted with whitespace character
2021.02.02: v2.5 - add XmlCheckIsValid toggle to force checking that the encoded XML is valid
2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
2020.10.28: v2.3 - add TrimWhiteSpace option
2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>]
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
To cast them to float64, first set flag with CastNanInf(true).
2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
2015.12.02: XML decoding/encoding that preserves original structure of document. See NewMapXmlSeq()
and mv.XmlSeq() / mv.XmlSeqIndent().
2015-05-20: New: mv.StringIndentNoTypeInfo().
Also, alphabetically sort map[string]interface{} values by key to prettify output for mv.Xml(),
mv.XmlIndent(), mv.StringIndent(), mv.StringIndentNoTypeInfo().
2014-11-09: IncludeTagSeqNum() adds "_seq" key with XML doc positional information.
(NOTE: PreserveXmlList() is similar and will be here soon.)
2014-09-18: inspired by NYTimes fork, added PrependAttrWithHyphen() to allow stripping hyphen from attribute tag.
2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
2014-04-28: ValuesForPath() and NewMap() now accept path with indexed array references.
<h4>Basic Unmarshal XML to map[string]interface{}</h4>
<pre>type Map map[string]interface{}</pre>
Create a Map value, 'mv', from any map[string]interface{} value, 'v':
Unmarshal / marshal XML as a Map value, 'mv':
Unmarshal XML from an io.Reader as a Map value, 'mv':
Marshal Map value, 'mv', to an XML Writer (io.Writer):
Also, for prettified output:
<pre>xmlValue, err := mv.XmlIndent(prefix, indent, ...) err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...) raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)</pre>Bulk process XML with error handling (note: handlers must return a boolean value):
<pre>err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error)) err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))</pre>Converting XML to JSON: see Examples for NewMapXml and HandleXmlReader.
There are comparable functions and methods for JSON processing.
Arbitrary structure values can be decoded to / encoded from Map values:
Get everything at once, irrespective of path depth:
<pre>leafnodes := mv.LeafNodes() leafvalues := mv.LeafValues()</pre>A new Map with whatever keys are desired can be created from the current Map and then encoded in XML
or JSON. (Note: keys can use dot-notation.)
The package is fairly well self-documented with examples.
Also, the subdirectory "examples" contains a wide range of examples, several taken from golang-nuts discussions.
<h4>XML parsing conventions</h4>Using NewMapXml()
- Attributes are parsed to
map[string]interface{}values by prefixing a hyphen,-, to the attribute label. (Unless overridden byPrependAttrWithHyphen(false)orSetAttrPrefix().) - If the element is a simple element and has attributes, the element value
is given the key
#textfor itsmap[string]interface{}representation. (See the 'atomFeedString.xml' test data, below.) - XML comments, directives, and process instructions are ignored.
- If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
Using NewMapXmlSeq()
- Attributes are parsed to
map["#attr"]map[<attr_label>]map[string]interface{}values where the<attr_label>value has "#text" and "#seq" keys - the "#text" key holds the value for<attr_label>. - All elements, except for the root, have a "#seq" key.
- Comments, directives, and process instructions are unmarshalled into the Map using the keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more specifics.)
- Name space syntax is preserved:
<ns:key>something</ns.key>parses tomap["ns:key"]interface{}{"something"}xmlns:ns="http://myns.com/ns"parses tomap["xmlns:ns"]interface{}{"http://myns.com/ns"}
Both
- By default, "Nan", "Inf", and "-Inf" values are not cast to float64. If you want them to be cast, set a flag to cast them using CastNanInf(true).
- 'nil'
Mapvalues, which may represent 'null' JSON values, are encoded as<tag/>. NOTE: the operation is not symmetric as<tag/>elements are decoded astag:""Mapvalues, which, then, encode in JSON as"tag":""values. - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one. (Go randomizes the walk through map[string]interface{} values.) If you plan to re-encode the Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
