3

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:

  • Train A starts in city 1, passes city 2 & 3, and arrives in city 4.
  • Train B starts in city 1, passes city 5 & 6 & 7, and arrives in city 8.

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"
Joshua Taylor
  • 80,876
  • 9
  • 135
  • 306
MikeB
  • 1,549
  • 2
  • 14
  • 26
  • 1
    You could use an RDF list (or other collection). Note that constructing RDF/XML by hand is not really recommended. RDF/XML is just a serialization format for RDF. It's usually easier to use an RDF library or human-readable format to write the RDF, and then just serialize in whatever format you need. – Joshua Taylor Apr 16 '15 at 10:42
  • 1
    But querying, you'd still only need one "optional" – Joshua Taylor Apr 16 '15 at 10:44
  • Please take a look at my edit. How can I achieve something like that without far to many optionals? I'm working with an architecture of someones elses master thesis for my bachelor. He decided to do everything manually so I'll have to work with what I've got... – MikeB Apr 16 '15 at 10:48

1 Answers1

3

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?.

Joshua Taylor
  • 80,876
  • 9
  • 135
  • 306
  • 1
    This is easily the best answer I have ever gotten on this website! Thank you so much, you're my hero! – MikeB Apr 16 '15 at 11:10
  • @MikeyB Incidently, there was a question not too long ago about representing train schedules. ANy chance that was by the other person you mentioned? – Joshua Taylor Apr 16 '15 at 11:19
  • @MikeyB Found it. Any chance it's related to [How to query the distance between two things with SPARQL](http://stackoverflow.com/q/24538144/1281433) or [W3C validator can't process RDF/XML](http://stackoverflow.com/q/24527728/1281433)? – Joshua Taylor Apr 16 '15 at 11:24
  • That does not look like the work of one out of my team. Has to be a coincidence. Might still be interesting for me. Thanks for that too – MikeB Apr 16 '15 at 11:29
  • 1
    @MikeyB No worries; just seemed similar (sophisticated representations of train lines), and the timing you mentioned (working on a codebase someone else had already set up earlier) made me wonder. Maybe they'll be interesting as background, then. Enjoy! – Joshua Taylor Apr 16 '15 at 11:31
  • @RobV Thanks for catching the highlighting bug; your edit didn't quite fix it though. The `` wasn't supposed to be indented; the problem was that the closing had been ` ->`, not `-->`. I know SQL and SPARQL aren't exactly the same, but it's relatively good syntax highlighting for SPARQL. – Joshua Taylor Apr 16 '15 at 16:01