-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsparql.go
130 lines (110 loc) · 2.98 KB
/
sparql.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package sparql
import (
"bytes"
"encoding/json"
"errors"
"io"
"strings"
"time"
"github.com/anglo-korean/rdf"
)
// DateFormat is the expected layout of the xsd:DateTime values. You can override
// it if your triple store uses a different layout.
var DateFormat = time.RFC3339
var xsdString rdf.IRI
func init() {
xsdString, _ = rdf.NewIRI("http://www.w3.org/2001/XMLSchema#string")
}
// Results holds the parsed results of a application/sparql-results+json response.
type Results struct {
Head header
Results results
}
type header struct {
Link []string
Vars []string
}
type results struct {
Distinct bool
Ordered bool
Bindings []map[string]binding
}
type binding struct {
Type string // "uri", "literal", "typed-literal" or "bnode"
Value string
Lang string `json:"xml:lang"`
DataType string
}
// ParseBytes takes a bytes containing valid sparql-results+json and
// returns a set of Results
func ParseBytes(b []byte) (*Results, error) {
return Parse(bytes.NewReader(b))
}
// ParseString takes a string containing valid sparql-results+json and
// returns a set of Results
func ParseString(s string) (*Results, error) {
return Parse(strings.NewReader(s))
}
// Parse takes an application/sparql-results+json response as an
// io.Reader (like from an http.Response.Body) and parses it
// into a Results struct
func Parse(r io.Reader) (res *Results, err error) {
res = new(Results)
err = json.NewDecoder(r).Decode(res)
return
}
// Bindings returns a map of the bound variables in the SPARQL response, where
// each variable points to one or more RDF terms.
func (r *Results) Bindings() map[string][]rdf.Term {
rb := make(map[string][]rdf.Term)
for _, v := range r.Head.Vars {
for _, b := range r.Results.Bindings {
t, err := termFromJSON(b[v])
if err == nil {
rb[v] = append(rb[v], t)
}
}
}
return rb
}
// Solutions returns a slice of the query solutions, each containing a map
// of all bindings to RDF terms.
func (r *Results) Solutions() []map[string]rdf.Term {
var rs []map[string]rdf.Term
for _, s := range r.Results.Bindings {
solution := make(map[string]rdf.Term)
for k, v := range s {
term, err := termFromJSON(v)
if err == nil {
solution[k] = term
}
}
rs = append(rs, solution)
}
return rs
}
// termFromJSON converts a SPARQL json result binding into a rdf.Term. Any
// parsing errors on typed-literal will result in a xsd:string-typed RDF term.
// TODO move this functionality to package rdf?
func termFromJSON(b binding) (rdf.Term, error) {
switch b.Type {
case "bnode":
return rdf.NewBlank(b.Value)
case "uri":
return rdf.NewIRI(b.Value)
case "literal":
// Untyped literals are typed as xsd:string
if b.Lang != "" {
return rdf.NewLangLiteral(b.Value, b.Lang)
}
return rdf.NewTypedLiteral(b.Value, xsdString), nil
case "typed-literal":
iri, err := rdf.NewIRI(b.DataType)
if err != nil {
return nil, err
}
return rdf.NewTypedLiteral(b.Value, iri), nil
default:
return nil, errors.New("unknown term type")
}
}