Serializzazione ipermedia con JSON-LD

1. Panoramica

JSON-LD è un formato RDF basato su JSON per rappresentare i dati collegati. Consente di estendere gli oggetti JSON esistenti con funzionalità ipermediali; in altre parole, la capacità di contenere collegamenti in modo leggibile dalla macchina.

In questo tutorial, esamineremo un paio di opzioni basate su Jackson per serializzare e deserializzare il formato JSON-LD direttamente nei POJO . Tratteremo anche i concetti di base di JSON-LD che ci consentiranno di comprendere gli esempi.

2. Concetti di base

La prima volta che vediamo un documento JSON-LD, notiamo che alcuni nomi di membri iniziano con il carattere @ . Queste sono parole chiave JSON-LD e i loro valori ci aiutano a comprendere il resto del documento.

Per navigare nel mondo di JSON-LD e per comprendere questo tutorial, dobbiamo essere consapevoli di quattro parole chiave:

  • @context è la descrizione dell'oggetto JSON che contiene una mappa valore-chiave di tutto ciò che serve per l'interpretazione del documento
  • @vocab è una possibile chiave in @context che introduce un vocabolario predefinito per rendere l' oggetto @context molto più breve
  • @id è la parola chiave per identificare i collegamenti come proprietà della risorsa per rappresentare il collegamento diretto alla risorsa stessa o come valore @type per contrassegnare qualsiasi campo come collegamento
  • @type è la parola chiave per identificare i tipi di risorsa a livello di risorsa o in @context ; ad esempio, per definire il tipo di risorse incorporate

3. Serializzazione in Java

Prima di continuare, dovremmo dare un'occhiata ai nostri tutorial precedenti per rinfrescare la nostra memoria su Jackson ObjectMapper , Jackson Annotations e Jackson Serializer personalizzati.

Avendo già familiarità con Jackson, potremmo renderci conto che potremmo facilmente serializzare due campi personalizzati in qualsiasi POJO come @id e @type utilizzando l' annotazione @JsonProperty . Tuttavia, scrivere il @context a mano potrebbe richiedere molto lavoro e anche soggetto a errori .

Pertanto, per evitare questo approccio soggetto a errori, diamo un'occhiata più da vicino a due librerie che potremmo usare per la generazione di @context . Sfortunatamente, nessuno di loro è in grado di generare tutte le funzionalità di JSON-LD, ma esamineremo anche i loro difetti in seguito.

4. Serializzazione con Jackson-Jsonld

Jackson-Jsonld è un modulo Jackson che consente l'annotazione di POJO in modo conveniente per generare documenti JSON-LD.

4.1. Dipendenze di Maven

Innanzitutto, aggiungiamo jackson-jsonld come dipendenza al pom.xml :

 com.io-informatics.oss jackson-jsonld 0.1.1 

4.2. Esempio

Quindi, creiamo il nostro POJO di esempio e annotiamolo per la generazione di @context :

@JsonldResource @JsonldNamespace(name = "s", uri = "//schema.org/") @JsonldType("s:Person") @JsonldLink(rel = "s:knows", name = "knows", href = "//example.com/person/2345") public class Person { @JsonldId private String id; @JsonldProperty("s:name") private String name; // constructor, getters, setters }

Decostruiamo i passaggi per capire cosa abbiamo fatto:

  • Con @JsonldResource abbiamo contrassegnato il POJO per l'elaborazione come risorsa JSON-LD
  • In @JsonldNamespace abbiamo definito una scorciatoia per il vocabolario che vogliamo usare
  • Il parametro che abbiamo specificato in @JsonldType diventerà il @type della risorsa
  • Abbiamo utilizzato l' annotazione @JsonldLink per aggiungere collegamenti alla risorsa. Una volta elaborato, il parametro name verrà utilizzato come nome di campo e aggiunto anche come chiave al @context. href sarà il valore del campo e rel sarà il valore mappato in @context
  • Il campo che abbiamo contrassegnato con @JsonldId diventerà @id della risorsa
  • Il parametro che abbiamo specificato in @JsonldProperty diventerà il valore mappato al nome del campo nel @context

Quindi, generiamo il documento JSON-LD.

Per prima cosa, dovremmo registrare il JsonldModule in ObjectMapper . Questo modulo contiene un serializzatore personalizzato che Jackson utilizzerà per i POJO contrassegnati con l' annotazione @JsonldResource .

Quindi, continueremo e utilizzeremo ObjectMapper per generare il documento JSON-LD:

ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JsonldModule()); Person person = new Person("//example.com/person/1234", "Example Name"); String personJsonLd = objectMapper.writeValueAsString(person);

Di conseguenza, la variabile personJsonLd dovrebbe ora contenere:

{ "@type": "s:Person", "@context": { "s": "//schema.org/", "name": "s:name", "knows": { "@id": "s:knows", "@type": "@id" } }, "name": "Example Name", "@id": "//example.com/person/1234", "knows": "//example.com/person/2345" }

4.3. Considerazioni

Prima di scegliere questa libreria per un progetto, dovremmo considerare quanto segue:

  • Non è possibile utilizzare la parola chiave @vocab , quindi dovremo utilizzare @JsonldNamespace per fornire una scorciatoia per la risoluzione dei nomi dei campi o scrivere ogni volta l' IDI (Internationalized Resource Identifier) ​​completo
  • Possiamo definire i collegamenti solo in fase di compilazione, quindi per aggiungere un runtime di collegamento, dovremmo usare la riflessione per modificare quel parametro nell'annotazione

5. Serializzazione con Hydra-Jsonld

Hydra-Jsonld è un modulo della libreria Hydra-Java, creata principalmente per abilitare la creazione di risposte JSON-LD conveniente per le applicazioni Spring. Utilizza il vocabolario Hydra per rendere i documenti JSON-LD più espressivi.

Tuttavia , il modulo Hydra-Jsonld contiene un serializzatore Jackson e alcune annotazioni che possiamo utilizzare per generare documenti JSON-LD al di fuori di Spring Framework .

5.1. Dipendenze di Maven

Per prima cosa, aggiungiamo la dipendenza per hydra-jsonld a pom.xml :

 de.escalon.hypermedia hydra-jsonld 0.4.2 

5.2. Esempio

In secondo luogo, annotiamo il nostro POJO per la generazione di @context .

Hydra-Jsonld genera automaticamente un @context predefinito senza la necessità di annotazioni. Se siamo soddisfatti delle impostazioni predefinite, dobbiamo solo aggiungere @id per ottenere un documento JSON-LD valido.

The default vocabulary will be the schema.org vocabulary, the @type the Java class name, and the public properties of the POJO will all be included in the resulting JSON-LD document.

In this example, let's override these defaults with custom values:

@Vocab("//example.com/vocab/") @Expose("person") public class Person { private String id; private String name; // constructor @JsonProperty("@id") public String getId() { return id; } @Expose("fullName") public String getName() { return name; } }

Again, let’s take a closer look at the steps involved:

  • Compared to the Jackson-Jsonld example, we left out the knows field from our POJO because of the limitations of Hydra-Jsonld outside of the Spring Framework
  • We set our preferred vocabulary with the @Vocab annotation
  • By using the @Expose annotation on the class, we set a different resource @type
  • We used the same @Expose annotation on a property to set its mapping to a custom value in the @context
  • For generating the @id from a property, we used the @JsonProperty annotation from Jackson

Next, let's configure an instance of a Jackson Module that we can register in the ObjectMapper. We'll add the JacksonHydraSerializer as a BeanSerializerModifier so it can be applied to all POJOs that are being serialized:

SimpleModule getJacksonHydraSerializerModule() { return new SimpleModule() { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.addBeanSerializerModifier(new BeanSerializerModifier() { @Override public JsonSerializer modifySerializer( SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) { if (serializer instanceof BeanSerializerBase) { return new JacksonHydraSerializer((BeanSerializerBase) serializer); } else { return serializer; } } }); } }; }

Then let's register the Module in ObjectMapper and use it. We should also set the ObjectMapper to only include non-null values to produce a valid JSON-LD document:

ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(getJacksonHydraSerializerModule()); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); Person person = new Person("//example.com/person/1234", "Example Name"); String personJsonLd = objectMapper.writeValueAsString(person);

Now, the personJsonLd variable should contain:

{ "@context": { "@vocab": "//example.com/vocab/", "name": "fullName" }, "@type": "person", "name": "Example Name", "@id": "//example.com/person/1234" }

5.3. Considerations

Although it's technically possible to use Hydra-Jsonld outside of the Spring Framework, it was originally designed for usage with Spring-HATEOAS. As a result, there's no way to generate links with annotations as we saw in Jackson-Jsonld. On the other hand, they are generated for some Spring-specific classes automatically.

Before we choose this library for a project, we should consider the following:

  • Using it with the Spring Framework will enable additional features
  • There's no easy way to generate links if we're not using the Spring Framework
  • We cannot disable the usage of @vocab, we can only override it

6. Deserialization With Jsonld-Java and Jackson

Jsonld-Java is the Java implementation of the JSON-LD 1.0 specification and API, which is unfortunately not the latest version.

For an implementation of the 1.1 specification version, have a look at the Titanium JSON-LD library.

To deserialize a JSON-LD document, let's transform it with a JSON-LD API feature, called compaction, to a format that we can map to a POJO with ObjectMapper.

6.1. Maven Dependencies

First, let's add the dependency for jsonld-java:

 com.github.jsonld-java jsonld-java 0.13.0 

6.2. Example

Let's work with this JSON-LD document as our input:

{ "@context": { "@vocab": "//schema.org/", "knows": { "@type": "@id" } }, "@type": "Person", "@id": "//example.com/person/1234", "name": "Example Name", "knows": "//example.com/person/2345" }

For the sake of simplicity, let's assume we have the content of the document in a String variable called inputJsonLd.

First, let's compact it and convert it back to a String:

Object jsonObject = JsonUtils.fromString(inputJsonLd); Object compact = JsonLdProcessor.compact(jsonObject, new HashMap(), new JsonLdOptions()); String compactContent = JsonUtils.toString(compact);
  • We can parse and write the JSON-LD object with methods from the JsonUtils, which is part of the Jsonld-Java library
  • When using the compact method, as a second parameter we can use an empty Map. This way, the compaction algorithm will produce a simple JSON object where the keys are resolved to their IRI forms

The compactContent variable should contain:

{ "@id": "//example.com/person/1234", "@type": "//schema.org/Person", "//schema.org/knows": { "@id": "//example.com/person/2345" }, "//schema.org/name": "Example Name" }

In secondo luogo, personalizziamo il nostro POJO con le annotazioni di Jackson per adattarlo a tale struttura del documento:

@JsonIgnoreProperties(ignoreUnknown = true) public class Person { @JsonProperty("@id") private String id; @JsonProperty("//schema.org/name") private String name; @JsonProperty("//schema.org/knows") private Link knows; // constructors, getters, setters public static class Link { @JsonProperty("@id") private String id; // constructors, getters, setters } }

E infine, mappiamo il JSON-LD al POJO:

ObjectMapper objectMapper = new ObjectMapper(); Person person = objectMapper.readValue(compactContent, Person.class);

7. Conclusione

In questo articolo, abbiamo esaminato due librerie basate su Jackson per serializzare un POJO in un documento JSON-LD e un modo per deserializzare un JSON-LD in un POJO.

Come abbiamo evidenziato, entrambe le librerie di serializzazione presentano dei difetti che dovremmo considerare prima di utilizzarle. Se abbiamo bisogno di utilizzare più funzionalità di JSON-LD di quelle che queste librerie possono offrire, potremmo avvicinarci alla creazione del nostro documento tramite una libreria RDF con formato di output JSON-LD.

Come al solito, il codice sorgente può essere trovato su GitHub.