Documentation ¶
Overview ¶
Gojax provides a json-ish and xml-ish tool to convert json-ish to xml-ish so you can use an xpath-ish expression to manipulate the resulting xml. If you can set aside the ishs, you can use this package to work with JSON and XML.
The Consequences of ish ¶
For JSON, the following are meant by the expression of 'ish' above:
- A single object is the only allowed root element
- For numbers, only sequences of digits possibly prefixed with a negative sign are supported, including zero prefixed numbers
- For strings, anything can be escaped, but unicode hexadecimal expressions are not supported
- Finally, for whitespace, anything that returns true from unicode.IsSpace is accepted
For XML, only the least is supported - hence the 'ish' above.
- Nodes can have names from any JSON string
- Attribute keys and values are any Golang string
- JSON arrays have each inner-element nested inside a node with the following format: `<n index="0">...</n>`, where the 0 is replaced with the element's index in the array
- The JSON values of true, false, and null all convert to XML content as those runes
- Also, XML attributes are sorted in the output
For XPath, also, only the least is supported.
- You can separate path parts with either a / or // as in `/firstPathPart/secondPathPart`
- A single / requires the successive path part to match the node name of a child of the current XMLNode
- The double // allows the successive path part to match any descendant node name and can occur as a prefix to any path part such as `/firstPathPart//secondPathPart“
- Filters are placed after node names in any path part following a syntax such as `/nodeName[k=v]`, where k is any attribute key (or any descendant node name) and v is any attribute value (or that same descendant node's AnyContent result) with both being required to match the entire key or value respectively
- As an example of a descendant node content filter, given structure like `{"a":[{"b":{"c":1}}, {"b":{"c":1}}, {"b":{"c":2}}]}` and a path like `//b[c=2]`, you could find the node `<b><c>2</c></b>`
- Filters also support two special keys: meta and position(). The meta special key only supports one value 'no_attrs', which will only match nodes that have no attributes. The position() special key supports digits as values and checks that the position of that node in it's parents slice of children matches the given value.
- In addition to checking an exact match for the childs position with an =, the position() special key supports > and < operators that filter nodes as expected.
- The wildcard * is supported and matches any node name
As noted below, the above results in some restrictions:
- Node names cannot contain [ or / (unless you use [ in the givenName parameter when calling NewXMLNode)
- Node attribute keys and values cannot contain [ or ] or = or > or < or /
Sample Code ¶
All that given, here is a simple sample usage:
package main import ( "fmt" "git.ondollo.com/gojax" ) func main() { r := gojax.NewXMLNode("root#123[a=1]") b := gojax.NewXMLNode("b") b.Add(gojax.NewXMLContentNode("2")) r.Add(b) c := gojax.NewXMLNode("c") d := gojax.NewXMLNode("d") d.Add(gojax.NewXMLContentNode("3")) c.Add(d) r.Add(c) fmt.Println(r) // <root a="1" id="123"><b>2</b><c><d>3</d></c></root> x, err := gojax.FromJSON("root", `{"a":{"b":1}}`) if err != nil { fmt.Println("Failed to build an XMLNode from the given JSON:", err) } fmt.Println(x.Path("//b")) // [<b>1</b>] <nil> x, err = gojax.FromJSON("root", `{"a":[{"b":"c"},{"b":"d"}]}`) if err != nil { fmt.Println("Failed to build an XMLNode from the given JSON:", err) } fmt.Println(x.Path("//n[index=0]")) // [<n index="0"><b>c</b></n>] <nil> } }
Index ¶
- Variables
- type Options
- type XMLNode
- func First(xns []*XMLNode, err error) (*XMLNode, error)
- func FirstOr(givenNode *XMLNode, givenPath string) *XMLNode
- func FromJSON(root string, givenJson string) (xn *XMLNode, returnedErr error)
- func FromJSONDebug(root string, givenJson string) (xn *XMLNode, returnedErr error)
- func FromJSONWithOptions(root string, givenJson string, givenOptions Options) (xn *XMLNode, returnedErr error)
- func FromXML(givenXML string) (*XMLNode, error)
- func FromXMLWithOptions(givenXML string, givenOptions Options) (*XMLNode, error)
- func Last(xns []*XMLNode, err error) (*XMLNode, error)
- func LastOr(givenNodes []*XMLNode, err error) *XMLNode
- func NewXMLContentNode(givenContent string) *XMLNode
- func NewXMLNode(givenName string) *XMLNode
- func Reprocess(givenNodes []*XMLNode, givenPath string) []*XMLNode
- func UnwrappedOr(givenJson string) []*XMLNode
- func UnwrappedOrWithOptions(givenJson string, givenOptions Options) []*XMLNode
- func (x *XMLNode) Add(xn *XMLNode)
- func (x *XMLNode) AddAll(xns []*XMLNode)
- func (x *XMLNode) AddBefore(xn *XMLNode) error
- func (x *XMLNode) AddCloneOf(nonChild *XMLNode)
- func (x *XMLNode) AnyContent() string
- func (x *XMLNode) Clone() *XMLNode
- func (x *XMLNode) CloneInto(parent *XMLNode) *XMLNode
- func (x *XMLNode) Cut() error
- func (x *XMLNode) Get(k string) string
- func (x *XMLNode) GetName() string
- func (x XMLNode) JSON() string
- func (x *XMLNode) Path(givenPath string) ([]*XMLNode, error)
- func (x *XMLNode) PathOr(givenPath string) []*XMLNode
- func (x *XMLNode) Prepend(xn *XMLNode) error
- func (x *XMLNode) Remove(xn *XMLNode) error
- func (x *XMLNode) Set(k string, v string)
- func (x *XMLNode) SetName(givenName string)
- func (x XMLNode) String() string
- func (x XMLNode) StringWithOptions(givenOptions Options) string
Constants ¶
This section is empty.
Variables ¶
DefaultTestOptions provide a reference to the default test Options
Functions ¶
This section is empty.
Types ¶
type Options ¶
Options allows setting any of three options to make minor adjustments to the output of this library. For functions ending in `WithOptions`, the final parameter will have the Options type.
When Debug is true additional data is available. Avoid using this. The default is false.
When OutputHeader is true, the string `<?xml version="1.0" encoding="utf-8"?>` will be output on any call to String for an XMLNode without a parent. The default is true.
When WrapArrayElements is true, each element in an array will be wrapped with an element that follows the format: `<n index="0">...</n>`, where the 0 is replaced with the element's index in the array. The default is true.
type XMLNode ¶
type XMLNode struct { Content string Attrs map[string]string // contains filtered or unexported fields }
XMLNode provides just enough structure for the subset of XML that gojax supports.
func First ¶
First returns the first XMLNode from a slice. The parameters are meant to match the Path return values. The error either came from Path or was a result of calling First on an empty slice of XMLNodes.
func FirstOr ¶
FirstOr will call Path on the givenNode passing in the givenPath, and either return the first match, or panic
func FromJSON ¶
FromJSON processes the givenJson and creates as much of an XML-ish structure as it can with root as the first XMLNode's name.
If there is any failure from invalid input, it will return as much of the resulting XMLNode structure as it can and have an error in the location where the invalid input would have been placed (wrapped in an element with a name 'error').
The following invalid input:
fmt.Println(gojax.FromJSON("j", `[1]`))
Would result in an output that contained:
<j><error>Invalid input: root must be an object</error></j>
func FromJSONDebug ¶
FromJSONDebug sets the Debug Option and passes all given args to FromJSONWithOptions and returns all results from FromJSONWithOptions
func FromJSONWithOptions ¶
func FromJSONWithOptions(root string, givenJson string, givenOptions Options) (xn *XMLNode, returnedErr error)
FromJSONWithOptions is the same as FromJSON, but allows passing custom Options in. You will likely want to just use FromJSON.
func FromXML ¶
FromXML will take xml-ish (see above) and produce an XMLNode structure to mimic. One goal with this method is to get JSON back out of an XMLNode structure with the JSON method.
func FromXMLWithOptions ¶
FromXMLWithOptions is the same as FromXML, but allows passing custom Options in. You will likely want to just use FromXML.
func Last ¶
Last returns the last XMLNode from a slice. The parameters are meant to match the Path return values. The error either came from Path or was a result of calling Last on an empty slice of XMLNodes.
func LastOr ¶
LastOr will return the last in a slice of XMLNode, or panic. The parameters are meant to match the Path return values. The error either came from Path or was a result of calling Last on an empty slice of XMLNodes.
func NewXMLContentNode ¶
NewXMLContentNode returns an XMLNode with the givenContent as it's Content
func NewXMLNode ¶
NewXMLNode returns an XMLNode with the givenName as it's name. There are some supported variations to express attributes, including an id, inside the givenName:
- You can set the id attribute with nodeName#idValue
- You can set attributes with nodeName[k=v]
- If any, the id needs to come before attrs and will override any attribute key 'id' set through [] notation
So, combining the above, you can set attrs and id with:
nodeName#idValue[k=v]
Additionally, the special attribute keys available in paths won't hold specific meaning here and are ignored.
Finally, no attempt is made to ensure the uniqueness of any id attribute within any XMLNode
func Reprocess ¶
Reprocess runs the givenPath on each of the XMLNodes in givenNodes, collecting and then returning a unique list of matches
func UnwrappedOr ¶
UnwrappedOr will call FromJSON passing in an internally known root and then return the contents inside that root, or panic
func UnwrappedOrWithOptions ¶ added in v0.8.0
UnwrappedOrWithOptions is the same as UnwrappedOr, but allows passing custom Options in. You will likely want to just use UnwrappedOr.
func (*XMLNode) Add ¶
Add appends the XMLNode xn as a child in the children XMLNode slice of the XMLNode x
func (*XMLNode) AddAll ¶
AddAll appends all of the XMLNodes xns as children to the XMLNode x
func (*XMLNode) AddBefore ¶
AddBefore will add xn as a sibling of x just before x's position in the list of the parents children and possibly return an error if x is the root. The XMLNode xn will be Cut before being added to x.
func (*XMLNode) AddCloneOf ¶
AddCloneOf calls Clone on the given nonChild and then calls Add on x passing in the new clone
func (*XMLNode) AnyContent ¶
AnyContent will return either the content inside x or, if x has only one child that is a content node, the content of that one
func (*XMLNode) Clone ¶
Clone recursively clones X and returns the new XMLNode detatched from the x's parent
func (*XMLNode) CloneInto ¶
CloneInto calls Clone on x and then calls Add on the given parent passing in the new clone
func (*XMLNode) Cut ¶
Cut will remove the XMLNode x from it's parent and possibly return an error if x is the root
func (*XMLNode) Get ¶
Get will the value v that is assigned to the attribute key k
func (*XMLNode) GetName ¶
GetName will return the calculated name of the node.
func (XMLNode) JSON ¶
JSON produces a JSON output from an XMLNode structure. Children are output in the same order they exist in in the XMLNode structure with matching names simply repeated.
func (*XMLNode) Path ¶
Path will take the givenPath and attempt to traverse the XMLNode structure. If it is successful it will return all XMLNodes that satisfy the constraints of the givenPath, or an empty XMLNode slice with an error.
At each node we can have some remainder of the givenPath (with double prioritized to disambiguate):
/ - return the node we're at //a - find all descendants with a name of 'a' and return them /a - find a child with a name of 'a' and return it //a/<rest> - recurse into all descendants with a name of 'a' and run Path(`/<rest>`) on every descendant - keep any full match /a/<rest> - recurse into a child with a name of 'a' and run Path(`/<rest>`) on that child - keep any full match
Attribute filters look like:
nodeName[k=v] - only match the node with a name of 'nodeName' if it has an attribute 'k' with a value of 'v'
Consequences of this approach:
- Node names cannot contain [ or / (unless you use [ in the givenName parameter when calling NewXMLNode)
- Node attribute keys and values cannot contain [ or ] or = or > or < or /
func (*XMLNode) PathOr ¶
PathOr will call Path on the givenNode passing in the givenPath, and either return all matches, or panic
func (*XMLNode) Prepend ¶
Prepend will add the XMLNode xn to the beginning of the children of x possibly returning an error
func (*XMLNode) Remove ¶
Remove will remove the XMLNode xn from x and possibly return an error if xn is not a child of x
func (*XMLNode) Set ¶
Set will assign the value v to the attribute key k
func (*XMLNode) SetName ¶
SetName will turn any valid givenName expression into the resulting name and attributes, including id.
func (XMLNode) String ¶
String will output XML from an XMLNode structure. If you'd like to output JSON, use the JSON method.