Skip to content

Commit

Permalink
The API is now simpler to use.
Browse files Browse the repository at this point in the history
The API is now solely located under the github.com/ChrisTrenkamp/xsel
package, and added convenience functions for ContextSettings.
  • Loading branch information
ChrisTrenkamp committed Dec 28, 2023
1 parent 2d199ef commit a57f3c8
Show file tree
Hide file tree
Showing 13 changed files with 775 additions and 678 deletions.
118 changes: 60 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ import (
"bytes"
"fmt"

"github.com/ChrisTrenkamp/xsel/exec"
"github.com/ChrisTrenkamp/xsel/grammar"
"github.com/ChrisTrenkamp/xsel/parser"
"github.com/ChrisTrenkamp/xsel/store"
"github.com/ChrisTrenkamp/xsel"
)

func main() {
Expand All @@ -31,12 +28,12 @@ func main() {
</root>
`

xpath := grammar.MustBuild(`/root/a`)
parser := parser.ReadXml(bytes.NewBufferString(xml))
cursor, _ := store.CreateInMemory(parser)
result, _ := exec.Exec(cursor, &xpath)
xpath := xsel.MustBuildExpr(`/root/a`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath)

fmt.Println(result) // This is an XML node.
fmt.Println(result)
// Output: This is an XML node.
}
```

Expand All @@ -49,10 +46,35 @@ import (
"bytes"
"fmt"

"github.com/ChrisTrenkamp/xsel/exec"
"github.com/ChrisTrenkamp/xsel/grammar"
"github.com/ChrisTrenkamp/xsel/parser"
"github.com/ChrisTrenkamp/xsel/store"
"github.com/ChrisTrenkamp/xsel"
)

func main() {
xml := `
<root xmlns="http://some.namespace.com">
<a xmlns="http://some.namespace.com">This is an XML node with a namespace prefix.</a>
</root>
`

xpath := xsel.MustBuildExpr(`/ns:root/ns:a`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, xsel.WithNS("ns", "http://some.namespace.com"))

fmt.Println(result)
// Output: This is an XML node with a namespace prefix.
}
```

## Binding variables

```go
package main

import (
"bytes"
"fmt"

"github.com/ChrisTrenkamp/xsel"
)

func main() {
Expand All @@ -64,17 +86,14 @@ func main() {
</root>
`

contextSettings := func(c *exec.ContextSettings) {
c.NamespaceDecls["ns"] = "http://some.namespace.com"
c.Variables[exec.Name("http://some.namespace.com", "mynum")] = exec.Number(3.14)
}
const NS = "http://some.namespace.com"

xpath := grammar.MustBuild(`//node()[. = $ns:mynum]`)
parser := parser.ReadXml(bytes.NewBufferString(xml))
cursor, _ := store.CreateInMemory(parser)
result, _ := exec.Exec(cursor, &xpath, contextSettings)
xpath := xsel.MustBuildExpr(`//node()[. = $ns:mynum]`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, xsel.WithNS("ns", NS), xsel.WithVariableNS(NS, "mynum", xsel.Number(3.14)))

fmt.Println(result) //3.14
fmt.Println(result)
// Output: 3.14
}
```

Expand All @@ -87,11 +106,7 @@ import (
"bytes"
"fmt"

"github.com/ChrisTrenkamp/xsel/exec"
"github.com/ChrisTrenkamp/xsel/grammar"
"github.com/ChrisTrenkamp/xsel/node"
"github.com/ChrisTrenkamp/xsel/parser"
"github.com/ChrisTrenkamp/xsel/store"
"github.com/ChrisTrenkamp/xsel"
)

func main() {
Expand All @@ -102,27 +117,23 @@ func main() {
</root>
`

isComment := func(context exec.Context, args ...exec.Result) (exec.Result, error) {
nodeSet, isNodeSet := context.Result().(exec.NodeSet)
isComment := func(context xsel.Context, args ...xsel.Result) (xsel.Result, error) {
nodeSet, isNodeSet := context.Result().(xsel.NodeSet)

if !isNodeSet || len(nodeSet) == 0 {
return exec.Bool(false), nil
return xsel.Bool(false), nil
}

_, isComment := nodeSet[0].Node().(node.Comment)
return exec.Bool(isComment), nil
}

contextSettings := func(c *exec.ContextSettings) {
c.FunctionLibrary[exec.Name("", "is-comment")] = isComment
_, isComment := nodeSet[0].Node().(xsel.Comment)
return xsel.Bool(isComment), nil
}

xpath := grammar.MustBuild(`//node()[is-comment()]`)
parser := parser.ReadXml(bytes.NewBufferString(xml))
cursor, _ := store.CreateInMemory(parser)
result, _ := exec.Exec(cursor, &xpath, contextSettings)
xpath := xsel.MustBuildExpr(`//node()[is-comment()]`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, xsel.WithFunction("is-comment", isComment))

fmt.Println(result) // This is a comment.
fmt.Println(result)
// Output: This is a comment.
}
```

Expand All @@ -135,11 +146,7 @@ import (
"bytes"
"fmt"

"github.com/ChrisTrenkamp/xsel/exec"
"github.com/ChrisTrenkamp/xsel/grammar"
"github.com/ChrisTrenkamp/xsel/node"
"github.com/ChrisTrenkamp/xsel/parser"
"github.com/ChrisTrenkamp/xsel/store"
"github.com/ChrisTrenkamp/xsel"
)

func main() {
Expand Down Expand Up @@ -186,21 +193,16 @@ func main() {
Customers []Customer `xsel:"NS:Customers/NS:Customer"`
}

contextSettings := func(c *exec.ContextSettings) {
c.NamespaceDecls["NS"] = "http://www.adventure-works.com"
}

xpath := grammar.MustBuild(`/NS:Root`)
parser := parser.ReadXml(bytes.NewBufferString(xml))
cursor, _ := store.CreateInMemory(parser)
result, _ := exec.Exec(cursor, &xpath, contextSettings)
contextSettings := xsel.WithNS("NS", "http://www.adventure-works.com")
xpath := xsel.MustBuildExpr(`/NS:Root`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, contextSettings)

customers := Customers{}
exec.Unmarshal(result, &customers, contextSettings) // Remember to check for errors
xsel.Unmarshal(result, &customers, contextSettings) // Remember to check for errors

fmt.Printf("%+v\n", customers)
//{Customers:[{Id:GREAL Name:Great Lakes Food Market ContactName:Howard Snyder Address:{Address:2732 Baker Blvd. City:Eugene Region:OR}}
// {Id:HUNGC Name:Hungry Coyote Import Store ContactName:Yoshi Latimer Address:{Address:City Center Plaza 516 Main St. City:Walla Walla Region:WA}}]}
// Output: {Customers:[{Id:GREAL Name:Great Lakes Food Market ContactName:Howard Snyder Address:{Address:2732 Baker Blvd. City:Eugene Region:OR}} {Id:HUNGC Name:Hungry Coyote Import Store ContactName:Yoshi Latimer Address:{Address:City Center Plaza 516 Main St. City:Walla Walla Region:WA}}]}
}
```

Expand Down Expand Up @@ -240,7 +242,7 @@ The XML equivalent will be:
`xsel` supplies a grep-like commandline utility for querying XML documents:

```
$ go get github.com/ChrisTrenkamp/xsel
$ go install github.com/ChrisTrenkamp/xsel/xsel@latest
$ xsel -h
Usage of xsel:
-a If the result is a NodeSet, print the string value of all the nodes instead of just the first
Expand Down
140 changes: 140 additions & 0 deletions doc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package xsel_test

import (
"bytes"
"fmt"

"github.com/ChrisTrenkamp/xsel"
)

func ExampleExec() {
xml := `
<root>
<a>This is an XML node.</a>
</root>
`

xpath := xsel.MustBuildExpr(`/root/a`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath)

fmt.Println(result)
// Output: This is an XML node.
}

func ExampleWithNS() {
xml := `
<root xmlns="http://some.namespace.com">
<a xmlns="http://some.namespace.com">This is an XML node with a namespace prefix.</a>
</root>
`

xpath := xsel.MustBuildExpr(`/ns:root/ns:a`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, xsel.WithNS("ns", "http://some.namespace.com"))

fmt.Println(result)
// Output: This is an XML node with a namespace prefix.
}

func ExampleWithVariableNS() {
xml := `
<root>
<node>2.50</node>
<node>3.14</node>
<node>0.30</node>
</root>
`

const NS = "http://some.namespace.com"

xpath := xsel.MustBuildExpr(`//node()[. = $ns:mynum]`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, xsel.WithNS("ns", NS), xsel.WithVariableNS(NS, "mynum", xsel.Number(3.14)))

fmt.Println(result)
// Output: 3.14
}

func ExampleWithFunction() {
xml := `
<root>
<a>This is an element.</a>
<!-- This is a comment. -->
</root>
`

isComment := func(context xsel.Context, args ...xsel.Result) (xsel.Result, error) {
nodeSet, isNodeSet := context.Result().(xsel.NodeSet)

if !isNodeSet || len(nodeSet) == 0 {
return xsel.Bool(false), nil
}

_, isComment := nodeSet[0].Node().(xsel.Comment)
return xsel.Bool(isComment), nil
}

xpath := xsel.MustBuildExpr(`//node()[is-comment()]`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, xsel.WithFunction("is-comment", isComment))

fmt.Println(result)
// Output: This is a comment.
}

func ExampleUnmarshal() {
xml := `
<Root xmlns="http://www.adventure-works.com">
<Customers>
<Customer CustomerID="GREAL">
<CompanyName>Great Lakes Food Market</CompanyName>
<ContactName>Howard Snyder</ContactName>
<ContactTitle>Marketing Manager</ContactTitle>
<FullAddress>
<Address>2732 Baker Blvd.</Address>
<City>Eugene</City>
<Region>OR</Region>
</FullAddress>
</Customer>
<Customer CustomerID="HUNGC">
<CompanyName>Hungry Coyote Import Store</CompanyName>
<ContactName>Yoshi Latimer</ContactName>
<FullAddress>
<Address>City Center Plaza 516 Main St.</Address>
<City>Walla Walla</City>
<Region>WA</Region>
</FullAddress>
</Customer>
</Customers>
</Root>
`

type Address struct {
Address string `xsel:"NS:Address"`
City string `xsel:"NS:City"`
Region string `xsel:"NS:Region"`
}

type Customer struct {
Id string `xsel:"@CustomerID"`
Name string `xsel:"NS:CompanyName"`
ContactName string `xsel:"NS:ContactName"`
Address Address `xsel:"NS:FullAddress"`
}

type Customers struct {
Customers []Customer `xsel:"NS:Customers/NS:Customer"`
}

contextSettings := xsel.WithNS("NS", "http://www.adventure-works.com")
xpath := xsel.MustBuildExpr(`/NS:Root`)
cursor, _ := xsel.ReadXml(bytes.NewBufferString(xml))
result, _ := xsel.Exec(cursor, &xpath, contextSettings)

customers := Customers{}
_ = xsel.Unmarshal(result, &customers, contextSettings) // Remember to check for errors

fmt.Printf("%+v\n", customers)
// Output: {Customers:[{Id:GREAL Name:Great Lakes Food Market ContactName:Howard Snyder Address:{Address:2732 Baker Blvd. City:Eugene Region:OR}} {Id:HUNGC Name:Hungry Coyote Import Store ContactName:Yoshi Latimer Address:{Address:City Center Plaza 516 Main St. City:Walla Walla Region:WA}}]}
}
4 changes: 0 additions & 4 deletions exec/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ type ContextSettings struct {
NamespaceDecls map[string]string
FunctionLibrary map[XmlName]Function
Variables map[XmlName]Result
// Context is the initial position to run XPath queries. This is initialized to the root of
// the document. This may be overridden to point to a different position in the document.
// When overriding the Context, it must be a Cursor contained within the root, or bad things can happen!
Context store.Cursor
}

type ContextApply func(c *ContextSettings)
Expand Down
Loading

0 comments on commit a57f3c8

Please sign in to comment.