Unable to get JsonIdentityInfo to work correctly

Viewed 299

I have a relationship between two objects. I have a Spring CLI communicating with a RESTful Web Service which uses Jackson. I am using the @JsonIdentityInfo annotation, but it is unable to create a relationship between the two classes. Additionally I am using WebClient

The first Object contains the following code:

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

@JsonSerialize(as= FuncUnit.class)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "fu_id", scope=FuncUnit.class)
@Setter @Getter 
public class FuncUnit  {

    @JsonProperty(value = "description")
    private String description;

    @Id @JsonProperty(value = "fu_id", required = true)
    private Long fu_id;

    // standard constructors    
    public FuncUnit (long fuId)
    {
        fu_id = fuId;
    }

    public FuncUnit() {}
}

The second Object is:

@JsonSerialize(as=Engine.class)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Engine.class)
@Setter @Getter 
public class Engine  {

    @JsonProperty(value = "id", required = true)
    private Long id;

    @JsonProperty(value = "func_unit")
    private FuncUnit func_unit;

    public Engine() {}
}

Now the json I receive is the following:

[
    {
        "id": 111,
        "functional_unit": {
            "description": "",
            "fu_id": 11,
        },
    },
    {
        "id": 112,
        "functional_unit": 11,
    }
]

And the webClient code I wrote is the following. (I also tried without ExchangeStrategies, and I came out with the same result):

        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
                .codecs(configurer ->
                        configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON)))
                .build();

        String url = cliUtils.getBaseUrl();
        WebClient webClient = WebClient.builder().exchangeStrategies(exchangeStrategies).build();
 
        UriComponentsBuilder builder = UriComponentsBuilder.newInstance().fromHttpUrl(url);

        List<Engine> units = webClient.get()
        .uri(builder.build().toUri())
        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .retrieve()
        .bodyToFlux(Engine.class)
        .collectList()
        .block();

When the webClient code is called, I don't receive any errors, but only one of the Engines has a FuncUnit. The other Engine contains a null as a FuncUnit.

1 Answers

Short answer: use .bodyToMono(new ParameterizedTypeReference<List>(){}) instead.

Long answer: I've met exactly the some problem. After a lot of research, none of the solutions work for me. Such as Customized ObjectMapper on the exchangeStrategy.

We know that JsonIdentityInfo serializes the first instance as full object and other same object as references to prevent circular reference issue.

[
    {
        "id": 111,
        "functional_unit": {   // <----- First, Fully serialized
            "description": "",
            "fu_id": 11,
        },
    },
    {
        "id": 112,
        "functional_unit": 11,  // <----- Second, reference Id
    }
]

However, this issue is not because of the Serialization/Deserialization but because of the behavior of Flux. Though we use the flux.collectList() to get all the list items, they actually returns one by one (non-blocking) and collects all. The second and following same items won't be able to see the first reference and inject the references. So I try using Mono<List<>> to force them return together and it worked.

Related