Deserialize JSON, содержащий (_links и _embedded) с использованием spring -hateoas

Я пытаюсь запустить очень простые json webservices, которые возвращают данные этой формы:

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

Как вы можете видеть, это довольно прямолинейно. У меня нет проблем с разбором ссылок, с моими POJO, расширяющими форму ResourceSupport. Вот как они выглядят:

ПользователиJson (корневой элемент)

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 ...]
}

Дело в том, что я ожидал, что jackson и spring будут достаточно умны, чтобы проанализировать свойство _embedded и заполнить атрибут UsersJson.users, но это не так.

Я пробовал различные вещи, которые я нашел в Интернете, но единственное, что я мог бы нормально работать, это создать новый класс, действующий как обернутая оболочка:

ПользователиJson (корневой элемент)

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

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

Встроенная "обертка"

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

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

Это работает, но я нахожу его довольно уродливым.

Тем не менее, хотя следующая конфигурация RestTemplate работала бы (особенно, когда я видел EmbeddedMapper в Jackson2HalModule), но это не так:

        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);

Может ли кто-нибудь сказать мне, что мне не хватает?

Ответ 1

Наконец, я нашел лучший способ использовать эти API-интерфейсы application/hal + json.

Spring hateoas фактически предоставляет клиента, почти готового к использованию: org.springframework.hateoas.client.Traverson.

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

Как видите, я избавился от UsersJson и UsersEmbeddedListJson.

Вот maven зависимости, которые я добавил

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

Ответ 2

Если бы добавить это в мой DTO:

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

так как ResourceSupport не имеет стандартного POJO/Json-signal setter/constructor для ссылок

Ответ 3

Мне пришлось расширить мой DTO POJO с помощью специального установщика, анализирующего полученный вывод Spring Hateos на стороне клиента REST, используя Джексона в качестве преобразователя:

href= "href", а REL - это "rel" в качестве констант.

@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]));
                    }
                }
        );
    }
}