Henrik Kjus Alstad Henrik Kjus Alstad - 4 months ago 31
JSON Question

Returning object oriented json from sparql queries

If I want to ask for some different properties A and B, in SPARQL,
where there is one unique A, and possibly several B's belonging to each A.

The resulting query that I get is on the form:

A1, B1
A1, B2
A1, B3
A1, B4
A2, B5


How I would want this query result to look, is more of the form:

A1: B1,B2,B3,B4
A2: B5


Is there a way of doing this in SPARQL? What is this called?

Answer

The JSON output for SPARQL is documented in SPARQL 1.1 Query Results JSON Format, and specifically section 3.2.2 Encoding RDF Terms describes how the RDF is encoded in the JSON. Note that the JSON output is really just for encoding the results of a SPARQL query, not for creating JSON objects that correspond to a particular object model. Your best bet is probably to take the results you're getting and manipulate them yourself. There are still a few things that might be helpful from the SPARQL side, though.

group_concat for combining values

That said, maybe the following can help you get something that will work a bit better for you. If you have data like this:

@prefix : <http://example.org/> .

:object :hasA1 :b1, :b2, :b3, :b4 ;
        :hasA2 :b5 .

and a query like this:

prefix : <http://example.org/>

select ?subject ?property ?object
where {
  values ?property { :hasA1 :hasA2 }
  ?subject ?property ?object .
}

you'll get results like:

$ arq --data data.n3 --query query.sparql
-------------------------------
| subject | property | object |
===============================
| :object | :hasA1   | :b4    |
| :object | :hasA1   | :b3    |
| :object | :hasA1   | :b2    |
| :object | :hasA1   | :b1    |
| :object | :hasA2   | :b5    |
-------------------------------

You can use group_concat to combine all the values for hasA1 into a single value with a query like:

prefix : <http://example.org/>

select ?subject ?property (group_concat(?object;separator=',') as ?cobject)
where {
  values ?property { :hasA1 :hasA2 }
  ?subject ?property ?object .
}
group by ?subject ?property 

and get results like:

$ arq --data data.n3 --query query.sparql
------------------------------------------------------------------------------------------------------------------
| subject | property | cobject                                                                                   |
==================================================================================================================
| :object | :hasA2   | "http://example.org/b5"                                                                   |
| :object | :hasA1   | "http://example.org/b4,http://example.org/b3,http://example.org/b2,http://example.org/b1" |
------------------------------------------------------------------------------------------------------------------

If you ask for the results in JSON format, you'll get the following output, which might work for you, depending on what kinds of entities your b1b4 are. Specifically, if they're strings, where concatenation makes sense, this could be fine. If they're something else, it's probably not so useful.

$ arq -out JSON --data data.n3 --query query.sparql
{
  "head": {
    "vars": [ "subject" , "property" , "cobject" ]
  } ,
  "results": {
    "bindings": [
      {
        "subject": { "type": "uri" , "value": "http://example.org/object" } ,
        "property": { "type": "uri" , "value": "http://example.org/hasA2" } ,
        "cobject": { "type": "literal" , "value": "http://example.org/b5" }
      } ,
      {
        "subject": { "type": "uri" , "value": "http://example.org/object" } ,
        "property": { "type": "uri" , "value": "http://example.org/hasA1" } ,
        "cobject": { "type": "literal" , "value": "http://example.org/b4,http://example.org/b3,http://example.org/b2,http://example.org/b1" }
      }
    ]
  }
}

group_concat with construct

There are JSON serializations of RDF out there, and while they may not be supported by SPARQL engines, you could use a construct query to generate some RDF that is structured more like your desired form, and then use an RDF serialization converter to convert to a JSON format. For instance, Jena's rdfcat supports RDF/JSON output. Using a construct query like this:

prefix : <http://example.org/>

construct { 
 ?subject ?property ?cobject
}
where {
  select ?subject ?property (group_concat(?object;separator=',') as ?cobject)
  where {
    values ?property { :hasA1 :hasA2 }
    ?subject ?property ?object .
  }
  group by ?subject ?property 
}

(See Can we combine CONSTRUCT with aggregates in SPARQL 1.1? on answers.semanticweb.com for an explanation of why there's a nested query in this query.) This generates RDF of the form:

$ arq --out RDF/XML --data data.n3 --query query.sparql 
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns="http://example.org/">
  <rdf:Description rdf:about="http://example.org/object">
    <hasA1>http://example.org/b4,http://example.org/b3,http://example.org/b2,http://example.org/b1</hasA1>
    <hasA2>http://example.org/b5</hasA2>
  </rdf:Description>
</rdf:RDF>

We can pipe that through rdfcat to get some RDF/JSON out, which may finally be closer to what you're looking for:

$ arq --out RDF/XML --data data.n3 --query query.sparql | rdfcat -out RDF/JSON /dev/stdin 
{ 
  "http://example.org/object" : { 
    "http://example.org/hasA2" : [ { 
      "type" : "literal" ,
      "value" : "http://example.org/b5"
    }
     ] ,
    "http://example.org/hasA1" : [ { 
      "type" : "literal" ,
      "value" : "http://example.org/b4,http://example.org/b3,http://example.org/b2,http://example.org/b1"
    }
     ]
  }
}