Сериализация на хипермедия с JSON-LD

1. Общ преглед

JSON-LD е базиран на JSON формат RDF за представяне на свързани данни. Позволява разширяване на съществуващи JSON обекти с хипермедийни възможности; с други думи, способността да съдържа връзки по машинно четим начин.

В този урок ще разгледаме няколко базирани на Джаксън опции за сериализиране и десериализиране на формата JSON-LD директно в POJO . Ще разгледаме и основните концепции на JSON-LD, които ще ни позволят да разберем примерите.

2. Основни понятия

Първият път, когато видим документ JSON-LD, забелязваме, че някои имена на членове започват със символа @ . Това са ключови думи JSON-LD и техните стойности ни помагат да разберем останалата част от документа.

За да се ориентираме в света на JSON-LD и да разберем този урок, трябва да сме наясно с четири ключови думи:

  • @context е описанието на JSON обекта, който съдържа карта ключ-стойност на всичко необходимо за тълкуването на документа
  • @vocab е възможен ключ в @context, който въвежда речник по подразбиране, за да направи обекта @context много по-кратък
  • @id е ключовата дума за идентифициране на връзки или като свойство на ресурс, което представлява пряката връзка към самия ресурс, или като стойност @type, за да маркира всяко поле като връзка
  • @type е ключовата дума за идентифициране на типове ресурси или на ниво ресурс, или в @context ; например, за да дефинирате вида на вградените ресурси

3. Сериализация в Java

Преди да продължим, трябва да разгледаме предишните ни уроци, за да освежим паметта си на Jackson ObjectMapper , Jackson Annotations и персонализирани Jackson Serializers.

Вече запознати с Джаксън, може да осъзнаем, че лесно можем да сериализираме две персонализирани полета във всеки POJO като @id и @type, като използваме анотацията @JsonProperty . Въпреки това, писането на @context на ръка може да бъде много работа, а също и често води до грешки .

Следователно, за да избегнем този склонен към грешки подход, нека разгледаме по-отблизо две библиотеки, които бихме могли да използваме за генериране на @context . За съжаление нито един от тях не е в състояние да генерира всички функции на JSON-LD, но ще разгледаме и недостатъците им по-късно.

4. Сериализация с Jackson-Jsonld

Jackson-Jsonld е Jackson модул, който позволява анотирането на POJO по удобен начин за генериране на JSON-LD документи.

4.1. Зависимости на Maven

Първо, нека добавим jackson-jsonld като зависимост към pom.xml :

 com.io-informatics.oss jackson-jsonld 0.1.1 

4.2. Пример

След това нека създадем нашия пример POJO и го анотираме за генериране на @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 }

Нека да деконструираме стъпките, за да разберем какво сме направили:

  • С @JsonldResource маркирахме POJO за обработка като JSON-LD ресурс
  • В @JsonldNamespace дефинирахме стенография за лексиката, която искаме да използваме
  • Параметърът, който посочихме в @JsonldType, ще стане @type на ресурса
  • Използвахме анотацията @JsonldLink, за да добавим връзки към ресурса. Когато се обработи, параметърът на името ще се използва като име на поле и също така ще се добави като ключ към @context. href ще бъде стойността на полето, а rel ще бъде картографираната стойност в @context
  • Полето, което маркирахме с @JsonldId, ще стане @id на ресурса
  • Параметърът, който посочихме в @JsonldProperty, ще се превърне в стойността, съпоставена с името на полето в @context

След това нека генерираме документа JSON-LD.

Първо, трябва да регистрираме JsonldModule в ObjectMapper . Този модул съдържа персонализиран сериализатор, който Jackson ще използва за POJO, маркирани с анотацията @JsonldResource .

След това ще продължим и ще използваме ObjectMapper за генериране на 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);

В резултат променливата personJsonLd сега трябва да съдържа:

{ "@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. Съображения

Преди да изберем тази библиотека за проект, трябва да разгледаме следното:

  • Използването на ключовата дума @vocab не е възможно, така че ще трябва да използваме @JsonldNamespace, за да предоставим стенография за разрешаване на имена на полета, или да изписваме пълния интернационализиран идентификатор на ресурса (IRI) всеки път
  • Можем да дефинираме връзки само по време на компилация, така че за да добавим време на изпълнение на връзката, ще трябва да използваме отражение, за да променим този параметър в анотацията

5. Сериализация с Hydra-Jsonld

Hydra-Jsonld е модул на библиотеката Hydra-Java, който е създаден предимно, за да даде възможност за удобно създаване на JSON-LD отговор за пролетни приложения. Той използва лексиката Hydra, за да направи документите JSON-LD по-изразителни.

Въпреки това , модула за Hydra-Jsonld съдържа Джексън Serializer и някои пояснения, които можем да използваме, за да генерира JSON-LD документи извън рамките на Spring .

5.1. Зависимости на Maven

Първо, нека добавим зависимостта за hydra-jsonld към pom.xml :

 de.escalon.hypermedia hydra-jsonld 0.4.2 

5.2. Пример

Второ, нека анотираме нашия POJO за генериране на @context .

Hydra-Jsonld автоматично генерира по подразбиране @context без необходимост от пояснения. Ако сме доволни от настройките по подразбиране, трябва само да добавим @id, за да получим валиден JSON-LD документ.

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" }

Второ, нека приспособим нашия POJO с анотации на Джаксън, за да се побере в такава структура на документа:

@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 } }

И накрая, нека картографираме JSON-LD към POJO:

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

7. Заключение

В тази статия разгледахме две базирани на Джаксън библиотеки за сериализиране на POJO в документ JSON-LD и един от начините за десериализиране на JSON-LD в POJO.

Както подчертахме, и двете библиотеки за сериализация имат недостатъци, които трябва да вземем предвид, преди да ги използваме. Ако трябва да използваме повече функции на JSON-LD, отколкото тези библиотеки могат да предложат, бихме могли да подходим към създаването на нашия документ чрез RDF библиотека с изходен формат JSON-LD.

Както обикновено, изходният код може да бъде намерен в GitHub.