/*
* Copyright (c) 2014. Escalon System-Entwicklung, Dietrich Schulten
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
* the specific language governing permissions and limitations under the License.
*/
package de.escalon.hypermedia.hydra.serialize;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.github.jsonldjava.core.JsonLdError;
import com.jayway.jsonassert.JsonAssert;
import de.escalon.hypermedia.hydra.JsonLdTestUtils;
import de.escalon.hypermedia.hydra.mapping.Expose;
import de.escalon.hypermedia.hydra.mapping.Term;
import de.escalon.hypermedia.hydra.mapping.Terms;
import de.escalon.hypermedia.hydra.mapping.Vocab;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
public class JacksonHydraSerializerTest {
private ObjectMapper mapper;
StringWriter w = new StringWriter();
@Before
public void setUp() {
mapper = new ObjectMapper();
// see https://github.com/json-ld/json-ld.org/issues/76
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.registerModule(new SimpleModule() {
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
public JsonSerializer<?> modifySerializer(
SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (serializer instanceof BeanSerializerBase) {
return new JacksonHydraSerializer(
(BeanSerializerBase) serializer);
} else {
return serializer;
}
}
});
}
});
}
@Test
public void testDefaultVocabIsRendered() throws Exception {
class Person {
private String name = "Dietrich Schulten";
public String getName() {
return name;
}
}
mapper.writeValue(w, new Person());
System.out.println(w);
assertEquals("{\"@context\":{" +
"\"@vocab\":\"http://schema.org/\"" +
"}" +
",\"@type\":\"Person\"," +
"\"name\":\"Dietrich Schulten\"}"
, w.toString());
}
@Vocab("http://xmlns.com/foaf/0.1/")
class Person {
private String name = "Dietrich Schulten";
public String getName() {
return name;
}
}
@Term(define = "common", as = "http://example.com/common#")
class Parent {
private String foo = "foo";
private Child baz = new Child();
@Expose("common:foo")
public String getFoo() {
return foo;
}
@Expose("common:baz")
public Child getBaz() {
return baz;
}
}
@Terms({
@Term(define = "child", as = "http://example.com/child#"),
@Term(define = "common", as = "http://example.com/common#")
})
class Child {
private String bar = "bar";
@Expose("child:bar")
public String getBar() {
return bar;
}
}
@Test
public void testDoesNotRepeatTerms() throws IOException {
mapper.writeValue(w, new Parent());
assertEquals("{\"@context\":{\"@vocab\":\"http://schema.org/\",\"common\":\"http://example.com/common#\"," +
"\"baz\":\"common:baz\",\"foo\":\"common:foo\"},\"@type\":\"Parent\",\"foo\":\"foo\"," +
"\"baz\":{\"@context\":{\"child\":\"http://example.com/child#\",\"bar\":\"child:bar\"}," +
"\"@type\":\"Child\",\"bar\":\"bar\"}}",
w.toString());
}
@Test
public void testFoafVocabIsRendered() throws Exception {
mapper.writeValue(w, new Person());
assertEquals("{\"@context\":{" +
"\"@vocab\":\"http://xmlns.com/foaf/0.1/\"" +
"}" +
",\"@type\":\"Person\"," +
"\"name\":\"Dietrich Schulten\"}"
, w.toString());
}
@Test
public void testAppliesPackageDefinedVocab() throws IOException {
mapper.writeValue(w, new de.escalon.hypermedia.hydra.beans.withvocab.Person("1964-08-08", "Schulten"));
assertEquals("{\"@context\":{" +
"\"@vocab\":\"http://xmlns.com/foaf/0.1/;\"," +
"\"surname\":\"http://schema.org/familyName\"" +
"}," +
"\"@type\":\"Person\"," +
"\"birthDate\":\"1964-08-08\"," +
"\"surname\":\"Schulten\"}", w.toString());
}
@Test
public void testAppliesPackageDefinedTerms() throws IOException, JsonLdError {
mapper.writeValue(w, new de.escalon.hypermedia.hydra.beans.withterms.Offer());
assertEquals("{\"@context\":{\"@vocab\":\"http://schema.org/\"," +
"\"gr\":\"http://purl.org/goodrelations/v1#\"," +
"\"dc\":\"http://purl.org/dc/elements/1.1/\"," +
"\"children\":{\"@reverse\":\"http://example.com/vocab#parent\"}," +
"\"businessFunction\":{" +
"\"@type\":\"@vocab\"}," +
"\"RENT\":\"gr:LeaseOut\"," +
"\"price\":\"gr:hasCurrencyValue\"}," +
"\"@type\":\"gr:Offering\"," +
"\"businessFunction\":\"RENT\"," +
"\"price\":1.99}", w.toString());
final String newline = System.getProperty("line.separator");
assertEquals("{" + newline +
" \"@type\" : \"http://purl.org/goodrelations/v1#Offering\"," + newline +
" \"http://purl.org/goodrelations/v1#hasCurrencyValue\" : 1.99," + newline +
" \"http://schema.org/businessFunction\" : {" + newline +
" \"@id\" : \"http://purl.org/goodrelations/v1#LeaseOut\"" + newline +
" }" + newline +
"}", JsonLdTestUtils.applyContext(w.toString()));
}
@Test
public void testNestedContextWithDifferentVocab() throws Exception {
@Vocab("http://purl.org/dc/elements/1.1/")
@Expose("BibliographicResource")
class Document {
public String title = "Moby Dick";
public Person creator = new Person();
}
mapper.writeValue(w, new Document());
assertEquals("{\"@context\":{" +
"\"@vocab\":\"http://purl.org/dc/elements/1.1/\"" +
"}" +
",\"@type\":\"BibliographicResource\"" +
",\"title\":\"Moby Dick\"" +
",\"creator\":{" +
"\"@context\":{" +
"\"@vocab\":\"http://xmlns.com/foaf/0.1/\"}" +
",\"@type\":\"Person\"" +
",\"name\":\"Dietrich Schulten\"}}"
, w.toString());
}
@Test
public void testDefaultVocabWithCustomTerm() throws Exception {
class Person {
public String birthDate;
public String firstName;
// override field name by schema.org property
@Expose("familyName")
public String lastName;
// override getter by schema.org property
@Expose("givenName")
public String getFirstName() {
return firstName;
}
public Person(String birthDate, String firstName, String lastName) {
this.birthDate = birthDate;
this.lastName = lastName;
this.firstName = firstName;
}
}
mapper.writeValue(w, new Person("1964-08-08", "Dietrich", "Schulten"));
assertEquals("{\"@context\":{" +
"\"@vocab\":\"http://schema.org/\"," +
"\"lastName\":\"familyName\"," +
"\"firstName\":\"givenName\"" +
"}," +
"\"@type\":\"Person\"," +
"\"birthDate\":\"1964-08-08\"," +
"\"firstName\":\"Dietrich\"," +
"\"lastName\":\"Schulten\"}"
, w.toString());
}
class Movie {
public String name = "Pirates of the Caribbean";
public String description = "Jack Sparrow and Barbossa embark on a quest.";
public String model = "http://www.imdb.com/title/tt0325980/";
public List<Offer> offers = Arrays.asList(new Offer());
}
@Term(define = "gr", as = "http://purl.org/goodrelations/v1#")
class Offer {
public BusinessFunction businessFunction = BusinessFunction.RENT;
public UnitPriceSpecification priceSpecification = new UnitPriceSpecification();
private DeliveryMethod availableDeliveryMethod = DeliveryMethod.DOWNLOAD;
public QuantitativeValue eligibleDuration = new QuantitativeValue();
public DeliveryMethod getAvailableDeliveryMethod() {
return availableDeliveryMethod;
}
public void setAvailableDeliveryMethod(DeliveryMethod availableDeliveryMethod) {
this.availableDeliveryMethod = availableDeliveryMethod;
}
}
enum DeliveryMethod {
@Expose("gr:DeliveryModeDirectDownload")
DOWNLOAD
}
enum BusinessFunction {
@Expose("gr:LeaseOut")
RENT,
@Expose("gr:Sell")
FOR_SALE,
@Expose("gr:Buy")
BUY
}
@Term(define = "gr", as = "http://purl.org/goodrelations/v1#")
class UnitPriceSpecification {
public BigDecimal price = BigDecimal.valueOf(3.99);
public String priceCurrency = "USD";
public String datetime = "2012-12-31T23:59:59Z";
}
class QuantitativeValue {
public String value = "30";
public String unitCode = "DAY";
}
@Test
public void testNestedSchemaOrgClassesWithGoodrelationsExtensions() throws IOException, JsonLdError {
final Movie movie = new Movie();
mapper.writeValue(w, movie);
final String json = w.toString();
System.out.println(json);
JsonAssert.with(json)
.assertThat("$.name", is(movie.name));
JsonAssert.with(json)
.assertThat("$.offers[0].availableDeliveryMethod", is(DeliveryMethod.DOWNLOAD.name()));
final String s = JsonLdTestUtils.applyContext(json);
System.out.println(s);
}
@Test
public void testSchemaOrgOfferWithGoodrelationsExtensions() throws IOException {
mapper.writeValue(w, new Offer());
assertEquals("{\"@context\":{" +
"\"@vocab\":\"http://schema.org/\"," +
"\"gr\":\"http://purl.org/goodrelations/v1#\"," +
"\"businessFunction\":{\"@type\":\"@vocab\"}," +
"\"RENT\":\"gr:LeaseOut\"," +
"\"availableDeliveryMethod\":{\"@type\":\"@vocab\"}," +
"\"DOWNLOAD\":\"gr:DeliveryModeDirectDownload\"}," +
"\"@type\":\"Offer\"," +
"\"businessFunction\":\"RENT\"," +
"\"priceSpecification\":{" +
"\"@type\":\"UnitPriceSpecification\"," +
"\"price\":3.99," +
"\"priceCurrency\":\"USD\"," +
"\"datetime\":\"2012-12-31T23:59:59Z\"}," +
"\"availableDeliveryMethod\":\"DOWNLOAD\"," +
"\"eligibleDuration\":{" +
"\"@type\":\"QuantitativeValue\"," +
"\"value\":\"30\"," +
"\"unitCode\":\"DAY\"}}", w.toString());
}
@Test
public void testSchemaOrgOfferWithNullEnum() throws IOException {
final Offer offer = new Offer();
offer.setAvailableDeliveryMethod(null);
mapper.writeValue(w, offer);
assertEquals("{\"@context\":{" +
"\"@vocab\":\"http://schema.org/\"," +
"\"gr\":\"http://purl.org/goodrelations/v1#\"," +
"\"businessFunction\":{\"@type\":\"@vocab\"}," +
"\"RENT\":\"gr:LeaseOut\"}," +
"\"@type\":\"Offer\"," +
"\"businessFunction\":\"RENT\"," +
"\"priceSpecification\":{" +
"\"@type\":\"UnitPriceSpecification\"," +
"\"price\":3.99," +
"\"priceCurrency\":\"USD\"," +
"\"datetime\":\"2012-12-31T23:59:59Z\"}," +
"\"eligibleDuration\":{" +
"\"@type\":\"QuantitativeValue\"," +
"\"value\":\"30\"," +
"\"unitCode\":\"DAY\"}}", w.toString());
}
@Test
public void testDoesNotRepeatContextIfUnnecessary() throws IOException {
mapper.writeValue(w, new Offer());
assertEquals("{\"@context\":" +
"{\"@vocab\":\"http://schema.org/\"," +
"\"gr\":\"http://purl.org/goodrelations/v1#\"," +
"\"businessFunction\":{\"@type\":\"@vocab\"}," +
"\"RENT\":\"gr:LeaseOut\"," +
"\"availableDeliveryMethod\":{\"@type\":\"@vocab\"}," +
"\"DOWNLOAD\":\"gr:DeliveryModeDirectDownload\"}," +
"\"@type\":\"Offer\"," +
"\"businessFunction\":\"RENT\"," +
"\"priceSpecification\":{\"@type\":\"UnitPriceSpecification\"," +
"\"price\":3.99," +
"\"priceCurrency\":\"USD\"," +
"\"datetime\":\"2012-12-31T23:59:59Z\"}," +
"\"availableDeliveryMethod\":\"DOWNLOAD\"," +
"\"eligibleDuration\":{\"@type\":\"QuantitativeValue\",\"value\":\"30\",\"unitCode\":\"DAY\"}}",
w.toString());
}
}