MikeB
MikeB

Reputation: 1659

Dynamic Array in RDF/XML

In c# I am generating a graph and am using RDF/XML to send it to my android application. Part of my graph is supposed to show different trains that arrive at a certain train station. Adding a train, its arrival, departure, destination and so on is no problem. Unfortunately I don't know how to get all stations the different trains pass on their way in the RDF/XML.

For example:

How can I dynamically add arrays like [city 5, city 6, city 7] to a train?

My current RDF/XML:

    <?xml version="1.0" encoding="utf-16"?>" +
                "<!DOCTYPE rdf:RDF [\n" +
                "\t<!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\n" +
                "]>\n" +
                "<rdf:RDF xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema#\" xmlns:ns0=\"http://my.url.com/ontologies/mash-up#\" xmlns:ns1=\"http://xmlns.com/foaf/0.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" +
                "  <ns0:Train rdf:about=\"http://my.url.com/ontologies/mash-up#Train-1d4b674c-479c-48a3-aab3-b729fc96cbd4\">\n" +
                "    <ns0:name rdf:datatype=\"&xsd;string\">RE 1</ns0:name>\n" +
                "    <ns0:description rdf:datatype=\"&xsd;string\">Platform 1</ns0:description>\n" +
                "    <ns0:arrival rdf:datatype=\"&xsd;dateTime\">2015-04-14T18:00:40Z</ns0:arrival>\n" +
                "    <ns0:departure rdf:datatype=\"&xsd;dateTime\">2015-04-14T18:02:40Z</ns0:departure>\n" +
                "    <ns0:destination rdf:datatype=\"&xsd;string\">Padaborn</ns0:destination>\n" +
                "    <ns1:primaryTopic rdf:resource=\"http://my.url.com/ontologies/mash-up#Train-1d4b674c-479c-48a3-aab3-b729fc96cbd4\" />\n" +
                "  </ns0:Train>\n" +
                "</rdf:RDF>";

Simply adding all stations as an own attribute would mean my Jena query would have to have very very many optionals and I'd like to avoid that.

Thanks in advance.

Edit: This is how I would do it. That would mean very many optionals...

" <ns0:stations rdf:about=\"http://my.url.com/ontologies/mash-up#Stations-xxxx\">“+
"   <ns0:from rdf:datatype=\"&xsd;string\">Aachen</ns0:from>\n“ +
"   <ns0:to rdf:datatype=\"&xsd;string\">Padaborn</ns0:to>\n“ +
"   <ns0:over1 rdf:datatype=\"&xsd;string\“>Köln</ns0:to>\n“ +
"   <ns0:over2 rdf:datatype=\"&xsd;string\“>Düsseldorf</ns0:to>\n“ +
"   <ns0:over3 rdf:datatype=\"&xsd;string\“>Duisburg</ns0:to>\n“ +
"   <ns0:over4 rdf:datatype=\"&xsd;string\“>Essen</ns0:to>\n“ +
"   <ns0:over5 rdf:datatype=\"&xsd;string\“>Dortmund</ns0:to>\n“ +
"   <ns0:over6 rdf:datatype=\"&xsd;string\“>Hamm (Westf)</ns0:to>\n“ +
" </ns0:stations>\n"

Upvotes: 3

Views: 1221

Answers (1)

Joshua Taylor
Joshua Taylor

Reputation: 85863

If order of the stations does not matter:

The simplest option, if the order of the stations passed is not important, would be to use the same property. That would look like this in the data (I'll use Turtle, since it's more human readable and writeable, but show the corresponding RDF/XML too; it's easy to convert between the two, since they're just different serializations of the same data):

@prefix : <urn:train:>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

:train1
  :name "RE 1" ;
  :description "Platform 1" ;
  :arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
  :departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
  :over "Station1", "Station2", "Station3" ;
  :destination "Padaborn" .
<rdf:RDF
    xmlns="urn:train:"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
  <rdf:Description rdf:about="urn:train:train1">
    <name>RE 1</name>
    <description>Platform 1</description>
    <arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:00:40Z</arrival>
    <departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:02:40Z</departure>
    <over>Station1</over>
    <over>Station2</over>
    <over>Station3</over>
    <destination>Padaborn</destination>
  </rdf:Description>
</rdf:RDF>

Then you can use just one optional in the SPARQL query:

prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>

select * where {
  ?train :name ?name ;
         :description ?description ;
         :arrival ?arrival ;
         :departure ?departure ;
         :destination ?destination .
  optional {
    ?train :over ?over
  }
}
--------------------------------------------------------------------------------------------------------------------------------------------
| train   | name   | description  | arrival                              | departure                            | destination | over       |
============================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station3" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station2" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station1" |
--------------------------------------------------------------------------------------------------------------------------------------------

You might want those in a list, so you'd group by and group_concat their values:

prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>

select ?train ?name ?description
       ?arrival ?departure
       ?destination
       (group_concat(?over) as ?overs)
where {
  ?train :name ?name ;
         :description ?description ;
         :arrival ?arrival ;
         :departure ?departure ;
         :destination ?destination .
  optional {
    ?train :over ?over
  }
}
group by ?train ?name ?description ?arrival ?departure ?destination
--------------------------------------------------------------------------------------------------------------------------------------------------------------
| train   | name   | description  | arrival                              | departure                            | destination | overs                        |
==============================================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station3 Station2 Station1" |
--------------------------------------------------------------------------------------------------------------------------------------------------------------

If the order matters

If the order is more important, then you'll need to preserve it somehow. You could either do this with distinct properties, or one property that has a list value (or some other structured data that supports ordering).

With multiple properties

Your solution that used over1, over2, over3, properties can actually be realized using just one optional pattern, since you can use a variable in place of a property, and then filter on the value of that. Just check whether its URI begins with the right prefix, including over:

@prefix : <urn:train:>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

:train1
  :name "RE 1" ;
  :description "Platform 1" ;
  :arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
  :departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
  :over1 "Station1" ;
  :over2 "Station2" ;
  :over3 "Station3" ;
  :destination "Padaborn" .
<rdf:RDF
    xmlns="urn:train:"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
  <rdf:Description rdf:about="urn:train:train1">
    <name>RE 1</name>
    <description>Platform 1</description>
    <arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:00:40Z</arrival>
    <departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:02:40Z</departure>
    <over1>Station1</over1>
    <over2>Station2</over2>
    <over3>Station3</over3>
    <destination>Padaborn</destination>
  </rdf:Description>
</rdf:RDF>
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>

select * where {
  ?train :name ?name ;
         :description ?description ;
         :arrival ?arrival ;
         :departure ?departure ;
         :destination ?destination .
  optional {
    ?train ?overProp ?over .
    filter strstarts(str(?overProp),str(:over))
  }
}
order by ?overProp
-------------------------------------------------------------------------------------------------------------------------------------------------------
| train   | name   | description  | arrival                              | departure                            | destination | overProp | over       |
=======================================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | :over1   | "Station1" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | :over2   | "Station2" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | :over3   | "Station3" |
-------------------------------------------------------------------------------------------------------------------------------------------------------

And of course, you can use group by to combine the values here in the same way.

With just a little bit of structure

You could also use just one property, but have the value be something with just a little bit of structure. E.g., a blank node that has a value for the station and the index in which it's passed:

@prefix : <urn:train:>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

:train1
  :name "RE 1" ;
  :description "Platform 1" ;
  :arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
  :departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
  :over [:station "Station1" ; :number 2 ] ,
        [:station "Station2" ; :number 3 ] ,
        [:station "Station3" ; :number 1 ] ;
  :destination "Padaborn" .
<rdf:RDF
    xmlns="urn:train:"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
  <rdf:Description rdf:about="urn:train:train1">
    <name>RE 1</name>
    <description>Platform 1</description>
    <arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:00:40Z</arrival>
    <departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:02:40Z</departure>
    <over rdf:parseType="Resource">
      <station>Station1</station>
      <number rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
      >2</number>
    </over>
    <over rdf:parseType="Resource">
      <station>Station2</station>
      <number rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
      >3</number>
    </over>
    <over rdf:parseType="Resource">
      <station>Station3</station>
      <number rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
      >1</number>
    </over>
    <destination>Padaborn</destination>
  </rdf:Description>
</rdf:RDF>
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

select * where {
  ?train :name ?name ;
         :description ?description ;
         :arrival ?arrival ;
         :departure ?departure ;
         :destination ?destination .
  optional {
    ?train :over [ :station ?over ;
                   :number ?number ]
  }
}
-----------------------------------------------------------------------------------------------------------------------------------------------------
| train   | name   | description  | arrival                              | departure                            | destination | over       | number |
=====================================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station3" | 1      |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station2" | 3      |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station1" | 2      |
-----------------------------------------------------------------------------------------------------------------------------------------------------

With a single property and a list

You could also have a single over property and have its value be a list. This is very easy to write in the N3/Turtle serialization. It's not as pretty in RDF/XML, but like I said in a comment, it's better to use a human writeable syntax if you're writing by hand, or to use a proper API for writing it. It's not too hard to query in the SPARQL either.

@prefix : <urn:train:>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

:train1
  :name "RE 1" ;
  :description "Platform 1" ;
  :arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
  :departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
  :over ("Station1" "Station2" "Station3") ;
  :destination "Padaborn" .
<rdf:RDF
    xmlns="urn:train:"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
  <rdf:Description rdf:about="urn:train:train1">
    <name>RE 1</name>
    <description>Platform 1</description>
    <arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:00:40Z</arrival>
    <departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2015-04-14T18:02:40Z</departure>
    <over rdf:parseType="Resource">
      <rdf:first>Station1</rdf:first>
      <rdf:rest rdf:parseType="Resource">
        <rdf:first>Station2</rdf:first>
        <rdf:rest rdf:parseType="Resource">
          <rdf:first>Station3</rdf:first>
          <rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
        </rdf:rest>
      </rdf:rest>
    </over>
    <destination>Padaborn</destination>
  </rdf:Description>
</rdf:RDF>
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

select * where {
  ?train :name ?name ;
         :description ?description ;
         :arrival ?arrival ;
         :departure ?departure ;
         :destination ?destination .
  optional {
    ?train :over/(rdf:rest*/rdf:first) ?over
  }
}
--------------------------------------------------------------------------------------------------------------------------------------------
| train   | name   | description  | arrival                              | departure                            | destination | over       |
============================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station1" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station2" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn"  | "Station3" |
--------------------------------------------------------------------------------------------------------------------------------------------

And of course, you can can group by and group_concat here, too. With the list based approach, you can actually compute the position of the element in the list, too. See, e.g., my answer to Is it possible to get the position of an element in an RDF Collection in SPARQL?.

Upvotes: 4

Related Questions