web-dev-qa-db-fra.com

Deserialize JSON contenant (_links et _embedded) en utilisant spring-hateoas

J'essaie d'invoquer des services Web JSON très simples qui renvoient des données de cette forme:

{
    "_embedded": {
        "users": [{
            "identifier": "1",
            "firstName": "John",
            "lastName": "Doe",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/test/users/1"
                }
            }
        },
        {
            "identifier": "2",
            "firstName": "Paul",
            "lastName": "Smith",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/test/users/2"
                }
            }
        }]
    },
    "_links": {
     "self": {
       "href": "http://localhost:8080/test/users"
     }
   },
   "page": {
     "size": 20,
     "totalElements": 2,
     "totalPages": 1,
     "number": 0
   }
}

Comme vous pouvez le constater, c'est assez simple. Je n'ai aucun problème à analyser les liens et à faire en sorte que mes POJO étendent le formulaire ResourceSupport. Voici à quoi ils ressemblent:

UsersJson (l'élément racine)

public class UsersJson extends ResourceSupport {
    private List<UserJson> users;

    [... getters and setters ...]
}

UserJson  

public class UserJson extends ResourceSupport {

    private Long identifier;

    private String firstName;

    private String lastName;

    [... getters and setters ...]
}

Le fait est que je m'attendais à ce que Jackson et Spring soient suffisamment intelligents pour analyser la propriété _embedded et remplir mon attribut UtilisateursJson.users, mais ce n'est pas le cas.

J'ai essayé diverses choses trouvées sur Internet, mais la seule chose que je pouvais faire pour bien fonctionner était de créer une nouvelle classe agissant comme un wrapper intégré:

UsersJson (l'élément racine)

public class UsersJson extends ResourceSupport {
    @JsonProperty("_embedded")
    private UsersEmbeddedListJson  embedded;

    [... getters and setters ...]
}

Emballé "wrapper"

public class UsersEmbeddedListJson extends ResourceSupport {
    private List<UserJson> users;

    [... getters and setters ...]
}

Cela fonctionne mais je le trouve assez moche.

Pourtant, j’imaginais que la configuration suivante de RestTemplate aurait fonctionné (en particulier lorsque j’ai vu EmbeddedMapper dans Jackson2HalModule), mais ce n’est pas le cas:

        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.registerModule(new Jackson2HalModule());

        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
        converter.setObjectMapper(mapper);

        RestTemplate restTemplate = new RestTemplate(Collections.singletonList(converter));

        ResponseEntity<UsersJson> result = restTemplate.getForEntity("http://localhost:8089/test/users", UsersJson.class, new HashMap<String, Object>());
        System.out.println(result);

Quelqu'un peut-il me dire ce qui me manque?

12
Heyjojo

Enfin, j'ai trouvé un meilleur moyen de consommer ces API application/hal + json.

Spring hateoas fournit en fait un client presque prêt à être utilisé: org.springframework.hateoas.client.Traverson.

Traverson traverson = new Traverson(new URI("http://localhost:8080/test"), MediaTypes.HAL_JSON);
TraversalBuilder tb = this.restTemplate.follow("users");
ParameterizedTypeReference<Resources<UserJson>> typeRefDevices = new ParameterizedTypeReference<Resources<UserJson>>() {};
Resources<UserJson> resUsers = tb.toObject(typeRefDevices);
Collection<UserJson> users= resUsers .getContent();

Comme vous pouvez le constater, je me suis débarrassé de UsersJson et de UsersEmbeddedListJson.

Voici les dépendances maven que j'ai ajoutées

    <dependency>
        <groupId>org.springframework.hateoas</groupId>
        <artifactId>spring-hateoas</artifactId>
        <version>0.19.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.plugin</groupId>
        <artifactId>spring-plugin-core</artifactId>
        <version>1.2.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.jayway.jsonpath</groupId>
        <artifactId>json-path</artifactId>
        <version>2.0.0</version>
    </dependency>
4
Heyjojo

A dû ajouter ceci à mon DTO:

@JsonProperty("_links")
public void setLinks(final Map<String, Link> links) {
    links.forEach((label, link) ->  add(link.withRel(label)) );
}

comme ResourceSupport n'a pas de setter/constructeur standard POJO/signalé par Json pour les liens

2
Whimusical

J'ai dû étendre mon POJO DTO avec un configurateur personnalisé analysant la sortie produite de Spring Hateos dans le client REST à l'aide de Jackson comme mappeur:

HREF= "href" etRELest "rel" en tant que constantes.

@JsonProperty("link")
public void setLink(final List<Map<String, String>> links) {
    if (links != null) {
        links.forEach(l ->
                {
                    final String[] rel = new String[1];
                    final String[] href = new String[1];
                    l.entrySet().stream().filter((e) -> e.getValue() != null)
                            .forEach(e -> {
                                if (e.getKey().equals(REL)) {
                                    rel[0] = e.getValue();
                                }
                                if (e.getKey().equals(HREF)) {
                                    href[0] = e.getValue();
                                }
                            });
                    if (rel[0] != null && href[0] != null) {
                        add(new Link(href[0], rel[0]));
                    }
                }
        );
    }
}
0
k_o_