-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathsparql.go
122 lines (105 loc) · 2.88 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
// Package sparql contains functions and data structures for querying SPARQL
// endpoints and parsing the response into RDF terms, as well as other
// convenience functions for working with SPARQL queries.
package sparql
import (
"encoding/json"
"errors"
"io"
"time"
"github.com/knakk/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
Boolean bool
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
}
// ParseJSON takes an application/sparql-results+json response and parses it
// into a Results struct.
func ParseJSON(r io.Reader) (*Results, error) {
var res Results
err := json.NewDecoder(r).Decode(&res)
return &res, err
}
// 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)
}
if b.DataType != "" {
dt, _ := rdf.NewIRI(b.DataType)
return rdf.NewTypedLiteral(b.Value, dt), nil
}
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")
}
}