Skip to content

Annotate your Java beans and serialize them as json-ld with hydra

License

Notifications You must be signed in to change notification settings

ceefour/hydra-java

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hydra-java Build Status

Annotate your Java beans and serialize them as json-ld with hydra.

Status: Testing

Latest release: 0.1.0

Problem

The meaning of json attributes in api responses, their possible values etc. is usually not obvious without referring to some information coming from outside the resource itself. That is due to the nature of json. Two solutions immediately come to mind. Both are ways of vendor-specific documentation, some are machine-readable, some aren’t.

Describe the type in some sort of json-schema, wadl, raml, swagger or similar and publish it together with the resource. People could even generate classes from this information, if they wish to. My api users coming from a wsdl background scream for something like that.

Or put up documentation pages to describe your ex:doodad extension relation types and make the documentation available by dereferencing http://example.com/api/rels#doodad.

But one of the rules for a ReSTful API is:

A REST API should never have “typed” resources that are significant to the client. Specification authors may use resource types for describing server implementation behind the interface, but those types must be irrelevant and invisible to the client. The only types that are significant to a client are the current representation’s media type and standardized relation names. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC’s functional coupling].

— Roy Fielding

My interpretation of this famous rant by Roy Fielding:

A publicly available media-type should give clients all necessary means to interpret a server response, and relation names for hyperlinks in the response must be recognizable based on standards, so that the client can act upon the responses it receives without knowing the details of a vendor-specific api.

In other words: If a client is told to make a reservation for a concert ticket, it should be able to recognize what one-fancy-api requires to achieve that without processing a vendor-specific documentation. How can we do that, purely based on a media type and relation names? Do we need hundreds of iana registered media types for all kinds of purposes?

Solution (evolving)

I see json-ld (media type application/ld+json) as a possible way to solve this problem without forcing people to ask me about my vendor-specific documentation, thus decoupling the clients from my server types.

Clients should be able to understand a response based on widely available, standardized, public information.

The json-ld mediatype allows to bring descriptions of things in the real world from public vocabularies into your json files. With json-ld there is a way to say that a json response describes a MusicEvent which offers a Ticket without any vendor-specific documentation, and it can also link to other resources.

A popular vocabulary which describes things on the internet is http://schema.org. It is used by all major search engines for search engine optimization and sufficient for basic needs. It also integrates with other vocabularies, e.g. by using additionalType to point to GoodRelations classes or by using external enumerated values as shown by DeliveryMethod.

(For those of you about to say that the Semantic Web never took off, please note that json-ld is not about the Semantic Web at all).

Hydra adds interaction to the mix. It describes exactly how to post a ticket reservation.

So I want to add json-ld information to json objects serialized from my Java beans.

Java beans have no knowledge about the meaning of their bean properties and they do not know what they represent in the real world.

In the simplest possible case I want to design my json objects so that they can be understood by others based on schema.org. By simply calling my json transfer class Person and letting it have an attribute name, I want to get a publicly understandable json object, like this:

    @Test
    public void testDefaultVocabIsRendered() throws Exception {

        class Person {
            private String name = "Dietrich Schulten";

            public String getName() {
                return name;
            }
        }

        mapper.writeValue(w, new Person());
    }

The corresponding json-ld object, written by hydra-java:

{
  "@context": {
    "@vocab": "http://schema.org/"
  },
  "@type": "Person",
  "name": "Dietrich Schulten"
}

Note that I do not bind my clients to a server type Person. Rather, client and server are talking about the thing Person as it is known and recognized by all major search engines.

For a more expressive example consider the json-ld example of MusicEvent, which shows how a ticket offering could look like.

In a more complex scenario I want to use my own attribute names and object design and still be able to use schema.org or other vocabs to describe their meaning. In json-ld I can. See below for a listing of vocabularies.

First Steps

It is currently possible to render responses from a spring-hateoas service based on Spring MVC.

Look into HydraMessageConverterTest to see how you can set up the hydra message converter with Spring MVC. The tests in JacksonHydraSerializerTest demonstrate the usage of @Vocab, @Expose and @Terms.

Features of hydra-spring

The conversion of a spring-hateoas Resource does the following:

  • renders a spring-hateoas List<Link> in a Resource<T> in json-ld style

  • renders spring-hateoas Resources<T> as hydra:Collection

  • renders response with "@vocab" : "http://schema.org/" by default, a different @vocab can be defined on a class or package using @Vocab.

  • supports vocabularies in addition to the default vocabulary via terms in the @context. Use @Term in conjunction with @Terms on a class or package for this.

  • renders @type based on the Java class name by default, a vocabulary class can be produced instead using @Expose on the Java class.

  • renders attributes assuming that the attribute name is a property in the default vocab defined by @vocab. In other words, it renders an offers member as "offers" on a json-ld object with a context defining "@vocab" : "http://schema.org", so that you end up with "http://schema.org/offers" as linked data name for your offers member. To map a custom attribute name such as foo to an existing property in the default vocab or other vocabs use @Expose on the attribute and a term will be created in @context which maps your attribute to the vocab property you set as value of @Expose.

  • renders Java enums assuming that an enum value name is an enumerated value defined by the default vocab. In json-ld it is not only possible to have attribute names, but also attribute values that have linked data names. The idiom to express that is "@type" : "@vocab". An example of this is OnSitePickup, which is an enum value for the property availableDeliveryMethod. If your Java enum value is ON_SITE_PICKUP, it matches the vocab value of OnSitePickup. It will be rendered as ON_SITE_PICKUP and hydra-java will add the necessary definition to the context which makes it clear that ON_SITE_PICKUP is actually http://schema.org/OnSitePickup. If your Java enum value has a different name than the vocab value, use @Expose on the enum value to get a correct representation in the context. Note that you can also expose an enum value from a different vocabulary such as GoodRelations, see below.

As a short demonstration consider the following example for @Expose and @Term.

The example shows a Java enum named `BusinessFunctionˋ whose enum values are exposed as values from GoodRelations. The enum appears on an Offer object with a GoodRelations term:

    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 Offer {
        public BusinessFunction businessFunction;
        ...
    }

The json-ld output written by hydra-java makes the GoodRelations url known under the shorthand gr, says that the businessFunction property contains values defined by a vocabulary and maps the Java enum value RENT to its linked data name "gr:LeaseOut".

{
    "@context": {
      "@vocab": "http://schema.org/"
      "gr": "http://purl.org/goodrelations/v1#",
      "businessFunction": {"@type": "@vocab"},
      "RENT": "gr:LeaseOut",
    },
    "@type": "Offer",
    "businessFunction": "RENT"
}

Maven Support

These are the maven coordinates for hydra-spring:

<dependency>
  <groupId>de.escalon.hypermedia</groupId>
  <artifactId>hydra-spring</artifactId>
  <version>0.1.0</version>
</dependency>

Vocabularies

What if schema.org is not sufficient? On Linked Open Vocabularies you can search for terms in other vocabularies. Another option is to propose an addition to schema.org.

If you are unsure which vocab to use, ask on the hydra mailing list.

Acknowledgements

I would like to thank Mike Amundsen, Stu Charlton, Jon Moore, Jørn Wildt, Mike Kelly, Markus Lanthaler, Gregg Kellog and Manu Sporny for their inspiration and for valuable comments along the way. Also thanks to Oliver Gierke who has been accepting some of my pull requests to spring-hateoas.

About

Annotate your Java beans and serialize them as json-ld with hydra

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 100.0%