mxj

package module
v0.0.0-...-de91dc4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 1, 2015 License: BSD-3-Clause, MIT Imports: 14 Imported by: 0

README

mxj - to/from maps, XML and JSON

Marshal/Unmarshal XML to/from JSON and `map[string]interface{}` values, and extract/modify values from maps by key or key-path, including wildcards.

mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.

Notices

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.

Basic Unmarshal XML / JSON / struct

type Map map[string]interface{}

Create a Map value, 'm', from any map[string]interface{} value, 'v':

m := Map(v)

Unmarshal / marshal XML as a Map value, 'm':

m, err := NewMapXml(xmlValue) // unmarshal
xmlValue, err := m.Xml()      // marshal

Unmarshal XML from an io.Reader as a Map value, 'm':

m, err := NewMapReader(xmlReader)         // repeated calls, as with an os.File Reader, will process stream
m, raw, err := NewMapReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded

Marshal Map value, 'm', to an XML Writer (io.Writer):

err := m.XmlWriter(xmlWriter)
raw, err := m.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter

Also, for prettified output:

xmlValue, err := m.XmlIndent(prefix, indent, ...)
err := m.XmlIndentWriter(xmlWriter, prefix, indent, ...)
raw, err := m.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)

Bulk process XML with error handling (note: handlers must return a boolean value):

err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error))
err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))

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:

m, err := NewMapStruct(structVal)
err := m.Struct(structPointer)

Extract / modify Map values

To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON or structure to a `Map` value, 'm', or cast a `map[string]interface{}` value to a `Map` value, 'm', then:
paths := m.PathsForKey(key)
path := m.PathForKeyShortest(key)
values, err := m.ValuesForKey(key, subkeys)
values, err := m.ValuesForPath(path, subkeys)
count, err := m.UpdateValuesForPath(newVal, path, subkeys)

Get everything at once, irrespective of path depth:

leafnodes := m.LeafNodes()
leafvalues := m.LeafValues()

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.)

newMap := m.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
newXml := newMap.Xml()   // for example
newJson := newMap.Json() // ditto

Usage

The package is fairly well self-documented with examples. (http://godoc.org/github.com/clbanning/mxj)

Also, the subdirectory "examples" contains a wide range of examples, several taken from golang-nuts discussions.

XML parsing conventions

  • Attributes are parsed to map[string]interface{} values by prefixing a hyphen, -, to the attribute label. (Unless overridden by PrependAttrWithHyphen(false).)
  • If the element is a simple element and has attributes, the element value is given the key #text for its map[string]interface{} representation. (See the 'atomFeedString.xml' test data, below.)

XML encoding conventions

  • 'nil' Map values, which may represent 'null' JSON values, are encoded as <tag/>. NOTE: the operation is not symmetric as <tag/> elements are decoded as tag:"" Map values, which, then, encode in JSON as "tag":"" values.

Running "go test"

Because there are no guarantees on the sequence map elements are retrieved, the tests have been written for visual verification in most cases. One advantage is that you can easily use the output from running "go test" as examples of calling the various functions and methods.

Motivation

I make extensive use of JSON for messaging and typically unmarshal the messages into map[string]interface{} variables. This is easily done using json.Unmarshal from the standard Go libraries. Unfortunately, many legacy solutions use structured XML messages; in those environments the applications would have to be refitted to interoperate with my components.

The better solution is to just provide an alternative HTTP handler that receives XML messages and parses it into a map[string]interface{} variable and then reuse all the JSON-based code. The Go xml.Unmarshal() function does not provide the same option of unmarshaling XML messages into map[string]interface{} variables. So I wrote a couple of small functions to fill this gap and released them as the x2j package.

Over the next year and a half additional features were added, and the companion j2x package was released to address XML encoding of arbitrary JSON and map[string]interface{} values. As part of a refactoring of our production system and looking at how we had been using the x2j and j2x packages we found that we rarely performed direct XML-to-JSON or JSON-to_XML conversion and that working with the XML or JSON as map[string]interface{} values was the primary value. Thus, everything was refactored into the mxj package.

Documentation

Overview

Marshal/Unmarshal XML to/from JSON and map[string]interface{} values, and extract/modify values from maps by key or key-path, including wildcards.

mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j or mxj/j2x packages.

Note: this library was designed for processing ad hoc anonymous messages. Bulk processing large data sets may be much more efficiently performed using the encoding/xml or encoding/json packages from Go's standard library directly.

Note:

2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.

SUMMARY

type Map map[string]interface{}

Create a Map value, 'm', from any map[string]interface{} value, 'v':
   m := Map(v)

Unmarshal / marshal XML as a Map value, 'm':
   m, err := NewMapXml(xmlValue) // unmarshal
   xmlValue, err := m.Xml()      // marshal

Unmarshal XML from an io.Reader as a Map value, 'm':
   m, err := NewMapReader(xmlReader)         // repeated calls, as with an os.File Reader, will process stream
   m, raw, err := NewMapReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded

Marshal Map value, 'm', to an XML Writer (io.Writer):
   err := m.XmlWriter(xmlWriter)
   raw, err := m.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter

Also, for prettified output:
   xmlValue, err := m.XmlIndent(prefix, indent, ...)
   err := m.XmlIndentWriter(xmlWriter, prefix, indent, ...)
   raw, err := m.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)

Bulk process XML with error handling (note: handlers must return a boolean value):
   err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error))
   err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))

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:
   m, err := NewMapStruct(structVal)
   err := m.Struct(structPointer)

To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
or structure to a Map value, 'm', or cast a map[string]interface{} value to a Map value, 'm', then:
   paths := m.PathsForKey(key)
   path := m.PathForKeyShortest(key)
   values, err := m.ValuesForKey(key, subkeys)
   values, err := m.ValuesForPath(path, subkeys) // 'path' can be dot-notation with wildcards and indexed arrays.
   count, err := m.UpdateValuesForPath(newVal, path, subkeys)

Get everything at once, irrespective of path depth:
   leafnodes := m.LeafNodes()
   leafvalues := m.LeafValues()

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. 'oldKey' can also use wildcards and indexed arrays.)
   newMap := m.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
   newXml := newMap.Xml()   // for example
   newJson := newMap.Json() // ditto

XML PARSING CONVENTIONS

  • Attributes are parsed to map[string]interface{} values by prefixing a hyphen, '-', to the attribute label. (PrependAttrWithHyphen(false) will override this.)
  • If the element is a simple element and has attributes, the element value is given the key '#text' for its map[string]interface{} representation.

XML ENCODING CONVENTIONS

  • 'nil' Map values, which may represent 'null' JSON values, are encoded as "<tag/>". NOTE: the operation is not symmetric as "<tag/>" elements are decoded as 'tag:""' Map values, which, then, encode in JSON as '"tag":""' values..

Index

Examples

Constants

View Source
const (
	Cast         = true // for clarity - e.g., mxj.NewMapXml(doc, mxj.Cast)
	SafeEncoding = true // ditto - e.g., mv.Json(mxj.SafeEncoding)
)
View Source
const (
	DefaultElementTag = "element"
)
View Source
const (
	DefaultRootTag = "doc"
)
View Source
const (
	NoAttributes = true // suppress LeafNode values that are attributes
)

Variables

View Source
var JsonUseNumber bool

Parse numeric values as json.Number types - see encoding/json#Number

View Source
var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)

If XmlCharsetReader != nil, it will be used to decode the XML, if required.

  import (
	     charset "code.google.com/p/go-charset/charset"
	     github.com/clbanning/mxj
	 )
  ...
  mxj.XmlCharsetReader = charset.NewReader
  m, merr := mxj.NewMapXml(xmlValue)
View Source
var XmlWriterBufSize int = 256

XmlWriterBufSize - set the size of io.Writer for the TeeReader used by NewMapXmlReaderRaw() and HandleXmlReaderRaw(). This reduces repeated memory allocations and copy() calls in most cases.

Functions

func AnyXml

func AnyXml(v interface{}, tags ...string) ([]byte, error)

Encode arbitrary value as XML.

Note: unmarshaling the resultant XML may not return the original value, since tag labels may have been injected to create the XML representation of the value.

 Encode an arbitrary JSON object.
	package main

	import (
		"encoding/json"
		"fmt"
		"github.com/clbanning/mxj"
	)

	func main() {
		jsondata := []byte(`[
			{ "somekey":"somevalue" },
			"string",
			3.14159265,
			true
		]`)
		var i interface{}
		err := json.Unmarshal(jsondata, &i)
		if err != nil {
			// do something
		}
		x, err := mxj.AnyXmlIndent(i, "", "  ", "mydoc")
		if err != nil {
			// do something else
		}
		fmt.Println(string(x))
	}

	output:
		<mydoc>
		  <somekey>somevalue</somekey>
		  <element>string</element>
		  <element>3.14159265</element>
		  <element>true</element>
		</mydoc>

Alternative values for DefaultRootTag and DefaultElementTag can be set as: AnyXmlIndent( v, myRootTag, myElementTag).

func AnyXmlIndent

func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte, error)

Encode an arbitrary value as a pretty XML string. Alternative values for DefaultRootTag and DefaultElementTag can be set as: AnyXmlIndent( v, "", " ", myRootTag, myElementTag).

func HandleJsonReader

func HandleJsonReader(jsonReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error

Bulk process JSON using handlers that process a Map value.

'rdr' is an io.Reader for the JSON (stream).
'mapHandler' is the Map processing handler. Return of 'false' stops io.Reader processing.
'errHandler' is the error processor. Return of 'false' stops io.Reader  processing and returns the error.
Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
      This means that you can stop reading the file on error or after processing a particular message.
      To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
Example
package main

import ()

func main() {
	/*
		See: bulk_test.go for working example.
		Run "go test" in package directory then scroll back to find output.

		Basic logic for bulk JSON to XML processing is similar to that for
		bulk XML to JSON processing as outlined in the HandleXmlReader example.
		The test case is also a good example.
	*/
}
Output:

func HandleJsonReaderRaw

func HandleJsonReaderRaw(jsonReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error

Bulk process JSON using handlers that process a Map value and the raw JSON.

'rdr' is an io.Reader for the JSON (stream).
'mapHandler' is the Map and raw JSON - []byte - processor. Return of 'false' stops io.Reader processing.
'errHandler' is the error and raw JSON processor. Return of 'false' stops io.Reader processing and returns the error.
Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
      This means that you can stop reading the file on error or after processing a particular message.
      To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
Example
package main

import ()

func main() {
	/*
		See: bulkraw_test.go for working example.
		Run "go test" in package directory then scroll back to find output.

		Basic logic for bulk JSON to XML processing is similar to that for
		bulk XML to JSON processing as outlined in the HandleXmlReader example.
		The test case is also a good example.
	*/
}
Output:

func HandleXmlReader

func HandleXmlReader(xmlReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error

Bulk process XML using handlers that process a Map value.

'rdr' is an io.Reader for XML (stream)
'mapHandler' is the Map processor. Return of 'false' stops io.Reader processing.
'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
      This means that you can stop reading the file on error or after processing a particular message.
      To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
Example
package main

import ()

func main() {
	/*
		Bulk processing XML to JSON seems to be a common requirement.
		See: bulk_test.go for working example.
		     Run "go test" in package directory then scroll back to find output.

		The logic is as follows.

			// need somewhere to write the JSON.
			var jsonWriter io.Writer

			// probably want to log any errors in reading the XML stream
			var xmlErrLogger io.Writer

			// func to handle Map value from XML Reader
			func maphandler(m mxj.Map) bool {
				// marshal Map as JSON
				jsonVal, err := m.Json()
				if err != nil {
					// log error
					return false // stops further processing of XML Reader
				}

				// write JSON somewhere
				_, err = jsonWriter.Write(jsonVal)
				if err != nil {
					// log error
					return false // stops further processing of XML Reader
				}

				// continue - get next XML from Reader
				return true
			}

			// func to handle error from unmarshaling XML Reader
			func errhandler(errVal error) bool {
				// log error somewhere
				_, err := xmlErrLogger.Write([]byte(errVal.Error()))
				if err != nil {
					// log error
					return false // stops further processing of XML Reader
				}

				// continue processing
				return true
			}

			// func that starts bulk processing of the XML
				...
				// set up io.Reader for XML data - perhaps an os.File
				...
				err := mxj.HandleXmlReader(xmlReader, maphandler, errhandler)
				if err != nil {
					// handle error
				}
				...
	*/
}
Output:

func HandleXmlReaderRaw

func HandleXmlReaderRaw(xmlReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error

Bulk process XML using handlers that process a Map value and the raw XML.

'rdr' is an io.Reader for XML (stream)
'mapHandler' is the Map and raw XML - []byte - processor. Return of 'false' stops io.Reader processing.
'errHandler' is the error and raw XML processor. Return of 'false' stops io.Reader processing and returns the error.
Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
      This means that you can stop reading the file on error or after processing a particular message.
      To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
See NewMapXmlReaderRaw for comment on performance associated with retrieving raw XML from a Reader.
Example
package main

import ()

func main() {
	/*
		See: bulkraw_test.go for working example.
		Run "go test" in package directory then scroll back to find output.

		Basic logic for bulk XML to JSON processing is in HandleXmlReader example;
		the only major difference is in handler function signatures so they are passed
		the raw XML.  (Read documentation on NewXmlReader regarding performance.)
	*/
}
Output:

func IncludeTagSeqNum

func IncludeTagSeqNum(b bool)

IncludeTagSeqNum - include a "_seq":N key:value pair with each inner tag, denoting its position when parsed. E.g.,

	<Obj c="la" x="dee" h="da">
		<IntObj id="3"/>
		<IntObj1 id="1"/>
		<IntObj id="2"/>
		<StrObj>hello</StrObj>
	</Obj>

parses as:

	{
	Obj:{
		"-c":"la",
		"-h":"da",
		"-x":"dee",
		"intObj":[
			{
				"-id"="3",
				"_seq":"0" // if mxj.Cast is passed, then: "_seq":0
			},
			{
				"-id"="2",
				"_seq":"2"
			}],
		"intObj1":{
			"-id":"1",
			"_seq":"1"
			},
		"StrObj":{
			"#text":"hello", // simple element value gets "#text" tag
			"_seq":"3"
			}
		}
	}

func PrependAttrWithHyphen

func PrependAttrWithHyphen(v bool)

PrependAttrWithHyphen. Prepend attribute tags with a hyphen. Default is 'true'.

Note:
	If 'false', unmarshaling and marshaling is not symmetric. Attributes will be
	marshal'd as <attr_tag>attr</attr_tag> and may be part of a list.

func SetArraySize

func SetArraySize(size int) int

Adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath(). This can have the effect of significantly reducing memory allocation-copy functions for large data sets. Returns the initial buffer size.

func XmlDefaultEmptyElemSyntax

func XmlDefaultEmptyElemSyntax()

XmlDefaultEmptyElemSyntax() - <tag .../> rather than <tag ...></tag>. Return XML encoding for empty elements to the default package setting. Reverses effect of XmlGoEmptyElemSyntax().

func XmlGoEmptyElemSyntax

func XmlGoEmptyElemSyntax()

XmlGoEmptyElemSyntax() - <tag ...></tag> rather than <tag .../>.

Go's encoding/xml package marshals empty XML elements as <tag ...></tag>.  By default this package
encodes empty elements as <tag .../>.  If you're marshaling Map values that include structures
(which are passed to xml.Marshal for encoding), this will let you conform to the standard package.

Alternatively, you can replace the encoding/xml/marshal.go file in the standard libary with the
patched version in the "xml_marshal" folder in this package. Then use xml.SetUseNullEndTag(true)
to have all XML encoding use <tag .../> for empty elements.

Types

type LeafNode

type LeafNode struct {
	Path  string      // a dot-notation representation of the path with array subscripting
	Value interface{} // the value at the path termination
}

LeafNode - a terminal path value in a Map. For XML Map values it represents an attribute or simple element value - of type string unless Map was created using Cast flag. For JSON Map values it represents a string, numeric, boolean, or null value.

type Map

type Map map[string]interface{}

func New

func New() Map

Allocate a Map.

func NewMapJson

func NewMapJson(jsonVal []byte) (Map, error)

Just a wrapper on json.Unmarshal

Converting JSON to XML is a simple as:
	...
	mapVal, merr := mxj.NewMapJson(jsonVal)
	if merr != nil {
		// handle error
	}
	xmlVal, xerr := mapVal.Xml()
	if xerr != nil {
		// handle error
	}

NOTE: as a special case, passing a list, e.g., [{"some-null-value":"", "a-non-null-value":"bar"}], will be interpreted as having the root key 'object' prepended - {"object":[ ... ]} - to unmarshal to a Map. See mxj/j2x/j2x_test.go.

func NewMapJsonReader

func NewMapJsonReader(jsonReader io.Reader) (Map, error)

Retrieve a Map value from an io.Reader.

NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
      os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
      value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
      a JSON object.

func NewMapJsonReaderRaw

func NewMapJsonReaderRaw(jsonReader io.Reader) (Map, []byte, error)

Retrieve a Map value and raw JSON - []byte - from an io.Reader.

NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
      os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
      value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
      a JSON object and retrieve the raw JSON in a single call.

func NewMapStruct

func NewMapStruct(structVal interface{}) (Map, error)

Create a new Map value from a structure. Error returned if argument is not a structure or if there is a json.Marshal or json.Unmarshal error.

Only public structure fields are decoded in the Map value. Also, json.Marshal structure encoding rules
are followed for decoding the structure fields.
Example
package main

import (
	"fmt"
	"github.com/clbanning/mxj"
)

func main() {
	type str struct {
		IntVal   int     `json:"int"`
		StrVal   string  `json:"str"`
		FloatVal float64 `json:"float"`
		BoolVal  bool    `json:"bool"`
		private  string
	}
	strVal := str{IntVal: 4, StrVal: "now's the time", FloatVal: 3.14159, BoolVal: true, private: "Skies are blue"}

	mapVal, merr := mxj.NewMapStruct(strVal)
	if merr != nil {
		// handle error
	}

	fmt.Printf("strVal: %#v\n", strVal)
	fmt.Printf("mapVal: %#v\n", mapVal)
	// Note: example output is conformed to pass "go test".  "mxj_test" is example_test.go package name.

}
Output:

strVal: mxj_test.str{IntVal:4, StrVal:"now's the time", FloatVal:3.14159, BoolVal:true, private:"Skies are blue"}
mapVal: mxj.Map{"int":4, "str":"now's the time", "float":3.14159, "bool":true}

func NewMapXml

func NewMapXml(xmlVal []byte, cast ...bool) (Map, error)

NewMapXml - convert a XML doc into a Map (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)

If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.

Converting XML to JSON is a simple as:
	...
	mapVal, merr := mxj.NewMapXml(xmlVal)
	if merr != nil {
		// handle error
	}
	jsonVal, jerr := mapVal.Json()
	if jerr != nil {
		// handle error
	}

func NewMapXmlReader

func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error)

Get next XML doc from an io.Reader as a Map value. Returns Map value.

func NewMapXmlReaderRaw

func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error)

Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.

NOTES: 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
          using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
          See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
          data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
          you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
       2. The 'raw' return value may be larger than the XML text value.  To log it, cast it to a string.

func (Map) Copy

func (mv Map) Copy() (Map, error)

Return a copy of mv as a newly allocated Map. If the Map only contains string, numeric, map[string]interface{}, and []interface{} values, then it can be thought of as a "deep copy." Copying a structure (or structure reference) value is subject to the noted restrictions.

NOTE: If 'mv' includes structure values with, possibly, JSON encoding tags
      then only public fields of the structure are in the new Map - and with
      keys that conform to any encoding tag instructions. The structure itself will
      be represented as a map[string]interface{} value.
Example
package main

import (
	"fmt"
	"github.com/clbanning/mxj"
)

func main() {
	// Hand-crafted Map values that include structures do NOT Copy() as expected,
	// since to simulate a deep copy the original Map value is JSON encoded then decoded.

	type str struct {
		IntVal   int     `json:"int"`
		StrVal   string  `json:"str"`
		FloatVal float64 `json:"float"`
		BoolVal  bool    `json:"bool"`
		private  string
	}
	s := str{IntVal: 4, StrVal: "now's the time", FloatVal: 3.14159, BoolVal: true, private: "Skies are blue"}
	m := make(map[string]interface{}, 0)
	m["struct"] = interface{}(s)
	m["struct_ptr"] = interface{}(&s)
	m["misc"] = interface{}(`Now is the time`)

	mv := mxj.Map(m)
	cp, _ := mv.Copy()

	fmt.Printf("mv:%s\n", mv.StringIndent(2))
	fmt.Printf("cp:%s\n", cp.StringIndent(2))

}
Output:

mv:
    struct :[unknown] mxj_test.str{IntVal:4, StrVal:"now's the time", FloatVal:3.14159, BoolVal:true, private:"Skies are blue"}
    struct_ptr :[unknown] &mxj_test.str{IntVal:4, StrVal:"now's the time", FloatVal:3.14159, BoolVal:true, private:"Skies are blue"}
    misc :[string] Now is the time
cp:
    misc :[string] Now is the time
    struct :
      int :[float64] 4.00e+00
      str :[string] now's the time
      float :[float64] 3.14e+00
      bool :[bool] true
    struct_ptr :
      int :[float64] 4.00e+00
      str :[string] now's the time
      float :[float64] 3.14e+00
      bool :[bool] true

func (Map) Exists

func (mv Map) Exists(path string) bool

Checks whether the path exists

func (Map) Json

func (mv Map) Json(safeEncoding ...bool) ([]byte, error)

Just a wrapper on json.Marshal. If option safeEncoding is'true' then safe encoding of '<', '>' and '&' is preserved. (see encoding/json#Marshal, encoding/json#Encode)

func (Map) JsonIndent

func (mv Map) JsonIndent(prefix, indent string, safeEncoding ...bool) ([]byte, error)

Just a wrapper on json.MarshalIndent. If option safeEncoding is'true' then safe encoding of '<' , '>' and '&' is preserved. (see encoding/json#Marshal, encoding/json#Encode)

func (Map) JsonIndentWriter

func (mv Map) JsonIndentWriter(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) error

Writes the Map as pretty JSON on the Writer. If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.

func (Map) JsonIndentWriterRaw

func (mv Map) JsonIndentWriterRaw(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) ([]byte, error)

Writes the Map as pretty JSON on the Writer. []byte is the raw JSON that was written. If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.

func (Map) JsonWriter

func (mv Map) JsonWriter(jsonWriter io.Writer, safeEncoding ...bool) error

Writes the Map as JSON on the Writer. If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.

func (Map) JsonWriterRaw

func (mv Map) JsonWriterRaw(jsonWriter io.Writer, safeEncoding ...bool) ([]byte, error)

Writes the Map as JSON on the Writer. []byte is the raw JSON that was written. If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.

func (Map) LeafNodes

func (mv Map) LeafNodes(no_attr ...bool) []LeafNode

LeafNodes - returns an array of all LeafNode values for the Map. The option no_attr argument suppresses attribute values (keys with prepended hyphen, '-') as well as the "#text" key for the associated simple element value.

func (Map) LeafPaths

func (mv Map) LeafPaths(no_attr ...bool) []string

LeafPaths - all paths that terminate in LeafNode values.

func (Map) LeafValues

func (mv Map) LeafValues(no_attr ...bool) []interface{}

LeafValues - all terminal values in the Map.

func (Map) NewMap

func (mv Map) NewMap(keypairs ...string) (Map, error)

(Map)NewMap - create a new Map from data in the current Map.

'keypairs' are key mappings "oldKey:newKey" and specify that the current value of 'oldKey'
should be the value for 'newKey' in the returned Map.
	- 'oldKey' supports dot-notation as described for (Map)ValuesForPath()
	- 'newKey' supports dot-notation but with no wildcards, '*', or indexed arrays
	- "oldKey" is shorthand for for the keypair value "oldKey:oldKey"
	- "oldKey:" and ":newKey" are invalid keypair values
	- if 'oldKey' does not exist in the current Map, it is not written to the new Map.
	  "null" is not supported unless it is the current Map.
	- see newmap_test.go for several syntax examples

NOTE: mv.NewMap() == mxj.New().

func (Map) Old

func (mv Map) Old() map[string]interface{}

Cast a Map to map[string]interface{}

func (Map) PathForKeyShortest

func (mv Map) PathForKeyShortest(key string) string

Extract the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'.. Paths are strings using dot-notation.

func (Map) PathsForKey

func (mv Map) PathsForKey(key string) []string

Get all paths through Map, 'mv', (in dot-notation) that terminate with the specified key. Results can be used with ValuesForPath.

func (Map) Remove

func (mv Map) Remove(path string) error

Removes the path.

func (Map) RenameKey

func (mv Map) RenameKey(path string, newName string) error

RenameKey renames a key in a Map. It works only for nested maps. It doesn't work for cases when it buried in a list.

func (Map) SetValueForPath

func (mv Map) SetValueForPath(value interface{}, path string) error

Sets the value for the path

func (Map) StringIndent

func (mv Map) StringIndent(offset ...int) string

Pretty print a Map.

func (Map) StringIndentNoTypeInfo

func (mv Map) StringIndentNoTypeInfo(offset ...int) string

Pretty print a Map without the value type information - just key:value entries.

func (Map) Struct

func (mv Map) Struct(structPtr interface{}) error

Marshal a map[string]interface{} into a structure referenced by 'structPtr'. Error returned if argument is not a pointer or if json.Unmarshal returns an error.

json.Unmarshal structure encoding rules are followed to encode public structure fields.
Example
package main

import (
	"fmt"
	"github.com/clbanning/mxj"
)

func main() {
	type str struct {
		IntVal   int     `json:"int"`
		StrVal   string  `json:"str"`
		FloatVal float64 `json:"float"`
		BoolVal  bool    `json:"bool"`
		private  string
	}

	mapVal := mxj.Map{"int": 4, "str": "now's the time", "float": 3.14159, "bool": true, "private": "Somewhere over the rainbow"}

	var strVal str
	mverr := mapVal.Struct(&strVal)
	if mverr != nil {
		// handle error
	}

	fmt.Printf("mapVal: %#v\n", mapVal)
	fmt.Printf("strVal: %#v\n", strVal)
	// Note: example output is conformed to pass "go test".  "mxj_test" is example_test.go package name.

}
Output:

mapVal: mxj.Map{"int":4, "str":"now's the time", "float":3.14159, "bool":true, "private":"Somewhere over the rainbow"}
strVal: mxj_test.str{IntVal:4, StrVal:"now's the time", FloatVal:3.14159, BoolVal:true, private:""}

func (Map) UpdateValuesForPath

func (mv Map) UpdateValuesForPath(newVal interface{}, path string, subkeys ...string) (int, error)

Update value based on path and possible sub-key values. A count of the number of values changed and any error are returned. If the count == 0, then no path (and subkeys) matched.

'newVal' can be a Map or map[string]interface{} value with a single 'key' that is the key to be modified
             or a string value "key:value[:type]" where type is "bool" or "num" to cast the value.
'path' is dot-notation list of keys to traverse; last key in path can be newVal key
       NOTE: 'path' spec does not currently support indexed array references.
'subkeys' are "key:value[:type]" entries that must match for path node
            The subkey can be wildcarded - "key:*" - to require that it's there with some value.
            If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
            exclusion critera - e.g., "!author:William T. Gaddis".
Example
package main

import ()

func main() {
	/*

	   var biblioDoc = []byte(`
	   <biblio>
	   	<author>
	   		<name>William Gaddis</name>
	   		<books>
	   			<book>
	   				<title>The Recognitions</title>
	   				<date>1955</date>
	   				<review>A novel that changed the face of American literature.</review>
	   			</book>
	   			<book>
	   				<title>JR</title>
	   				<date>1975</date>
	   				<review>Winner of National Book Award for Fiction.</review>
	   			</book>
	   		</books>
	   	</author>
	   </biblio>`)

	   	...
	   	m, merr := mxj.NewMapXml(biblioDoc)
	   	if merr != nil {
	   		// handle error
	   	}

	   	// change 'review' for a book
	   	count, err := m.UpdateValuesForPath("review:National Book Award winner." "*.*.*.*", "title:JR")
	   	if err != nil {
	   		// handle error
	   	}
	   	...

	   	// change 'date' value from string type to float64 type
	   	// Note: the following is equivalent to m, merr := NewMapXml(biblioDoc, mxj.Cast).
	   	path := m.PathForKeyShortest("date")
	   	v, err := m.ValuesForPath(path)
	   	if err != nil {
	   		// handle error
	   	}
	   	var total int
	   	for _, vv := range v {
	   		oldVal := "date:" + vv.(string)
	   		newVal := "date:" + vv.(string) + ":num"
	   		n, err := m.UpdateValuesForPath(newVal, path, oldVal)
	   		if err != nil {
	   			// handle error
	   		}
	   		total += n
	   	}
	   	...
	*/
}
Output:

func (Map) ValueForPath

func (mv Map) ValueForPath(path string) (interface{}, error)

Returns the first found value for the path.

func (Map) ValueForPathString

func (mv Map) ValueForPathString(path string) (string, error)

Returns the first found value for the path as a string.

func (Map) ValueOrEmptyForPathString

func (mv Map) ValueOrEmptyForPathString(path string) string

Returns the first found value for the path as a string. If the path is not found then it returns an empty string.

func (Map) ValuesForKey

func (mv Map) ValuesForKey(key string, subkeys ...string) ([]interface{}, error)

Return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match. On error, the returned array is 'nil'. NOTE: 'key' can be wildcard, "*".

'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
          - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
          - For attributes prefix the label with a hyphen, '-', e.g., "-seq:3".
          - If the 'key' refers to a list, then "key:value" could select a list member of the list.
          - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
          - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
            exclusion critera - e.g., "!author:William T. Gaddis".

func (Map) ValuesForPath

func (mv Map) ValuesForPath(path string, subkeys ...string) ([]interface{}, error)

Retrieve all values for a path from the Map. If len(returned_values) == 0, then no match. On error, the returned array is 'nil'.

'path' is a dot-separated path of key values.
       - If a node in the path is '*', then everything beyond is walked.
       - 'path' can contain indexed array references, such as, "*.data[1]" and "msgs[2].data[0].field" -
         even "*[2].*[0].field".
'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
          - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
          - For attributes prefix the label with a hyphen, '-', e.g., "-seq:3".
          - If the 'path' refers to a list, then "tag:value" would return member of the list.
          - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
          - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
            exclusion critera - e.g., "!author:William T. Gaddis".

func (Map) Xml

func (mv Map) Xml(rootTag ...string) ([]byte, error)

Encode a Map as XML. The companion of NewMapXml(). The following rules apply.

  • The key label "#text" is treated as the value for a simple element with attributes.
  • Map keys that begin with a hyphen, '-', are interpreted as attributes. It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
  • Map value type encoding: > string, bool, float64, int, int32, int64, float32: per "%v" formating > []bool, []uint8: by casting to string > structures, etc.: handed to xml.Marshal() - if there is an error, the element value is "UNKNOWN"
  • Elements with only attribute values or are null are terminated using "/>".
  • If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible. Thus, `{ "key":"value" }` encodes as "<key>value</key>".
  • To encode empty elements in a syntax consistent with encoding/xml call UseGoXmlEmptyElementSyntax().

The attributes tag=value pairs are alphabetized by "tag". Also, when encoding map[string]interface{} values - complex elements, etc. - the key:value pairs are alphabetized by key so the resulting tags will appear sorted.

func (Map) XmlIndent

func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error)

Encode a map[string]interface{} as a pretty XML string. See Xml for encoding rules.

func (Map) XmlIndentWriter

func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error

Writes the Map as pretty XML on the Writer. See Xml() for encoding rules.

func (Map) XmlIndentWriterRaw

func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error)

Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written. See Xml() for encoding rules.

func (Map) XmlWriter

func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error

Writes the Map as XML on the Writer. See Xml() for encoding rules.

func (Map) XmlWriterRaw

func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error)

Writes the Map as XML on the Writer. []byte is the raw XML that was written. See Xml() for encoding rules.

type MapRaw

type MapRaw struct {
	M Map
	R []byte
}

func NewMapsFromJsonFileRaw

func NewMapsFromJsonFileRaw(name string) ([]MapRaw, error)

ReadMapsFromJsonFileRaw - creates an array of MapRaw from a file of JSON values.

func NewMapsFromXmlFileRaw

func NewMapsFromXmlFileRaw(name string) ([]MapRaw, error)

NewMapsFromXmlFileRaw - creates an array of MapRaw from a file of XML values. NOTE: the slice with the raw XML is clean with no extra capacity - unlike NewMapXmlReaderRaw(). It is slow at parsing a file from disk and is intended for relatively small utility files.

type Maps

type Maps []Map

func NewMaps

func NewMaps() Maps

func NewMapsFromJsonFile

func NewMapsFromJsonFile(name string) (Maps, error)

NewMapsFromXmlFile - creates an array from a file of JSON values.

func NewMapsFromXmlFile

func NewMapsFromXmlFile(name string) (Maps, error)

NewMapsFromXmlFile - creates an array from a file of XML values.

func (Maps) JsonFile

func (mvs Maps) JsonFile(file string, safeEncoding ...bool) error

JsonFile - write Maps to named file as JSON Note: the file will be created, if necessary; if it exists it will be truncated. If you need to append to a file, open it and use JsonWriter method.

func (Maps) JsonFileIndent

func (mvs Maps) JsonFileIndent(file, prefix, indent string, safeEncoding ...bool) error

JsonFileIndent - write Maps to named file as pretty JSON Note: the file will be created, if necessary; if it exists it will be truncated. If you need to append to a file, open it and use JsonIndentWriter method.

func (Maps) JsonString

func (mvs Maps) JsonString(safeEncoding ...bool) (string, error)

JsonString - analogous to mv.Json()

func (Maps) JsonStringIndent

func (mvs Maps) JsonStringIndent(prefix, indent string, safeEncoding ...bool) (string, error)

JsonStringIndent - analogous to mv.JsonIndent()

func (Maps) XmlFile

func (mvs Maps) XmlFile(file string) error

XmlFile - write Maps to named file as XML Note: the file will be created, if necessary; if it exists it will be truncated. If you need to append to a file, open it and use XmlWriter method.

func (Maps) XmlFileIndent

func (mvs Maps) XmlFileIndent(file, prefix, indent string) error

XmlFileIndent - write Maps to named file as pretty XML Note: the file will be created,if necessary; if it exists it will be truncated. If you need to append to a file, open it and use XmlIndentWriter method.

func (Maps) XmlString

func (mvs Maps) XmlString() (string, error)

XmlString - analogous to mv.Xml()

func (Maps) XmlStringIndent

func (mvs Maps) XmlStringIndent(prefix, indent string) (string, error)

XmlStringIndent - analogous to mv.XmlIndent()

Directories

Path Synopsis
j2x.go - For (mostly) backwards compatibility with legacy j2x package.
j2x.go - For (mostly) backwards compatibility with legacy j2x package.
x2j - For (mostly) backwards compatibility with legacy x2j package.
x2j - For (mostly) backwards compatibility with legacy x2j package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL