JSON-LD, Semantic Web i HATEOAS

Serwisy RESTowe powinny podawać dane semantyczne. To czwarty i najwyższy stopień dojrzałości naszego API zwany HATEOAS. Aby nie być związanym z XMLem dziś zajmiemy się semantyczną stroną danych w JSON czyli JSON Linked Data.

W poprzednim artykule opisałem semantyczność RDF. Na koniec zbudowaliśmy przykładowy dokument opisujący zbiór dwóch aut marki Opel. Dziś będziemy korzystać z tego przykładu ale najpierw krótkie wprowadzenie.

JSON i semantyka

Załóżmy, że mamy serwis, którego API podaje dane w formie JSON. Idąc dalej w przykłady motoryzacyjne niech to będzie np. komis samochodowy. Taki komis wystawia api, które pozwala na wyszukiwanie co jest do sprzedaży. Pod adresem komis-u-janusza/niemiec-plakal-jak-sprzedawal/1234 będzie dostępny taki pojazd:

{
   "model": "opel-corsa",
   "productionDate": "2006-01-01"
}

Mamy prosty dokument JSON, ale znów nie wiemy co może oznaczać każde pole. Z pomocą przychodzi JSON-LD. Możemy odwołać się do słownika, który powie nam co oznaczają wszystkie pola. Na początek:

{
   "@context": "http://adam.wroclaw.pl/opels.jsonld#",
   "model": "opel-corsa",
   "productionDate": "2006-01-01"
}

Czym jest tajemniczy kontekst? To trochę jak przestrzeń nazw w RDF. Od tej pory nazwy będą oznaczały to, co zdefiniowaliśmy w kontekście. I tak np “opel-corsa” daje nam “http://adam.wroclaw.pl/opels.jsonld#opel-corsa” i podobnie. Nasz adres to definicja z poprzedniego przykładu przekształcona do formatu JSON-LD.

Ale mamy tu nieścisłość. Przecież w naszej definicji nie wiemy co oznacza “name” albo “productionDate”. Musimy rozszerzyć nasz kontekst:

{
   "@context": {
      "model": "@type",
      "productionDate":"http://purl.org/vso/ns#productionDate",
      "opel-corsa": "http://adam.wroclaw.pl/files/opels.jsonld#opel-corsa"
   },
   "@id": "1234",
   "model": "opel-corsa",
   "productionDate": "2006-01-01"
}

Co tu się stało? Idziemy po kolei:

Najpierw dodaliśmy element “@context”. Dzięki temu parser semantyczny będzie wiedział w jakim kontekście ma rozumieć dane.

"model": "@type",

W pierwszym wierszu kontekstu zdefiniowaliśmy, że nazwa “model” oznacza tak naprawdę typ (klasę, zbiór) elementu.

"opel-corsa": "http://adam.wroclaw.pl/files/opels.jsonld#opel-corsa"

Następnie podaliśmy adres typu. Rozwiązujemy nazwę “opel-corsa” na pełen adres. Pod podanym adresem jest definicja z poprzedniego artykułu.

"productionDate":"http://purl.org/vso/ns#productionDate",

Powiedzieliśmy, że nazwę “productionDate” trzeba rozumieć według definicji zawartej w http://purl.org/vso/ns#productionDate.

"@id": "1234",

Na koniec dodaliśmy jeszcze identyfikator. W ten sposób semantycznie wiemy, że nasz obiekt posiada unikalny identyfikator.

Nie musimy używać kontekstu. To samo moglibyśmy zapisać bezpośrednio podając pełne identyfikatory w postaci IRI:

{
   "@id": "http://adam.wroclaw.pl/opels.jsonld#opel-corsa",
   "http://purl.org/vso/ns#productionDate": "2006-01-01"
}

Oba zapisy znaczą to samo. Ale w pierwszym nie musieliśmy zmieniać naszych danych. Często mamy już gotowe serwisy RESTowe i gotowych klientów. Zmiana formatu danych to duży kłopot. JSON-LD pozwala nam dodać kontekst czyli zdefiniować semantykę bez dotykania samych danych.

Różne formaty JSON-LD

JSON-LD to nie jeden ustalony format. Możemy używać przedrostków podobnie jak używaliśmy przestrzeni nazw w RDF. Możemy całość zapisać jako zestaw wierzchołków i powiązań w grafie. Możemy wprost podawać wszystkie adresy (identyfikatory) w elementach albo korzystać z kontekstu.

Przykładowo jeśli chcemy użyć przedrostków, możemy napisać tak:

{
   "@context": {
      "vso":"http://purl.org/vso/ns#",
      "opels":"http://adam.wroclaw.pl/files/opels.jsonld#"
   },
   "@id": "1234",
   "@type": "opels:opel-corsa",
   "vso:productionDate": "2006-01-01"
}

Całość przypomina nam przestrzenie nazw w XML. Dodatkowy bonus – w JSON-LD w wartościach też możemy używać przedrostków. XML nam na to nie pozwalał i musieliśmy korzystać z pomocy encji.

Możemy również zapisać ten sam dokument jako listę elementów grafu:

{
   "@graph": [
      {
         "@id": "1234",
         "@type": "http://adam.wroclaw.pl/files/opels.jsonld#opel-corsa",
         "http://purl.org/vso/ns#productionDate": "2006-01-01"
      }
   ]
}

Tutaj mamy tylko jeden element z zadanym id oraz dwiema relacjami. Nic nie stoi na przeszkodzie aby takich elementów było wiele. Co więcej, definicję “@graph” też możemy poprzedzić kontekstem, gdzie ustawimy przestrzenie nazw (przedrostki).

Warto pobawić się w JSON-LD Playground, gdzie możemy wpisać nasz kod i obejrzeć go w kilku układach JSON-LD.

JSON-LD a RDF

Dokumenty RDF można łatwo zamienić na JSON-LD. Wystarczy skorzystać z translatora RDF dostępnego online. W ten sposób RDF z poprzedniego artykułu został zamieniony na JSON-LD dostępny tutaj.

W wyniku dostajemy dokument JSON-LD w zapisie grafowym. Możemy teraz skorzystać z JSON-LD Playground aby zamienić go na dowolną formę.

Podsumowanie

W przykładzie zbudowaliśmy dokument semantyczny nie zmieniając zupełnie samej treści JSONa. Wszystkie aplikacje korzystające z naszego api będą nadal działać (o ile ignorują nieznane im elementy (np “@context” i “@id”).

Tworząc od nowa serwis RESTowy będziemy budować go inaczej. Najpierw stworzymy naszą ontologię czyli słownik znaczeń. Potem w kontekście odwołamy się do niego. Na sam koniec będziemy podawać dane, których semantyka będzie już znana.

W ten właśnie sposób łączymy HATEOAS czyli semantyczne dane z danymi w JSON. Korzystając z wybranych ontologii możemy tworzyć linki, których znaczenie będzie znane automatom.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *