Allanh
Allanh

Reputation: 507

Mapping the same Json key in different variables in the same object

I'm doing some changes in a microservice which has an object like the following:

@ToString
@Getter
@Setter
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonDeserialize(builder = UsageRate.UsageRateBuilder.class)
public class UsageRate implements Serializable {


private int rate;

@JsonProperty("rate")
private Float ratePPR;

@JsonPOJOBuilder(withPrefix = "")
public static class UsageRateBuilder {
}

}

I'm getting the information that I'm going to use from a Json. That Json has a key called rate and I want to use that rate in two different variables, an int rate and a Float ratePPR. The problem here is that I know I can't have two variables from the same Json in the same object, but I'm obligated to do it because I can't touch the variable rate, so I have created the ratePPR variable to use when I need it.

When I'm testing I get the following error:

Conflicting getter definitions for property "rate": UsageRate#getRate(0 params) vs UsageRate#getRatePPR(0 params) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:295) at com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1309) at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider

What can I do to create the new variable ratePPR with the value from rate JSon value. Another thing important to say is the attribute in the DB can be a fractional, so, I need to save in the first place the value in ratePPR and after of that I need to save the integer value in rate variable.

Upvotes: 1

Views: 1495

Answers (2)

LHCHIN
LHCHIN

Reputation: 4009

You're almost there because I noticed that you have added @JsonDeserialize to your UsageRate class. So the key is how to implement your custom deserializer. Here comes an example:

Step 1: Create your custom deserializer and map the field rate to both variables(rate and ratePPR).

class UsageRateDeserializer extends StdDeserializer<UsageRate> {
    public UsageRateDeserializer() {
        this(null);
    }

    protected UsageRateDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public UsageRate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
        JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
        int rate = jsonNode.get("rate").asInt();
        Float ratePPR = jsonNode.get("rate").floatValue();

        return new UsageRate(rate, ratePPR);
    }
}

Step 2: Annotate UsageRate with @JsonDeserialize(using = UsageRateDeserializer.class)

@ToString
@Getter
@Setter
@AllArgsConstructor
@JsonDeserialize(using = UsageRateDeserializer.class)
class UsageRate implements Serializable {
    private int rate;
    private Float ratePPR;
}

Step 3: Let Jackson do the rest for you

String jsonStr = "{\"rate\": 50}";

ObjectMapper objectMapper = new ObjectMapper();
UsageRate usageRate = objectMapper.readValue(jsonStr, UsageRate.class);
System.out.println(usageRate.toString());

Console output:

UsageRate{rate=50, ratePPR=50.0}

Upvotes: 0

dariosicily
dariosicily

Reputation: 4547

The issue is due to the fact that your class rate and ratePPR fields match the same json rate property either in the deserialization process or serialization process, so as precondition you have to change the name of one of them. One way to solve the problem as from the user Lino's comment below your question is define a JsonCreator annotated constructor with a float parameter annotated with the @JsonProperty("rate") and inside define the two fields:

@ToString
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UsageRate implements Serializable {

    @JsonCreator
    public UsageRate(@JsonProperty("rate") float rate) {
        this.rateIntegerPart = (int) rate;
        this.ratePPR = rate;
    }

    @JsonIgnore
    private int rateIntegerPart;

    @JsonProperty("rate")
    private Float ratePPR;
}

The JsonIgnore annotation excludes the rateIntegerPart from the serialization :

//json = {"rate":10.0}
UsageRate rate = mapper.readValue(json, UsageRate.class);
System.out.println(rate); //it prints UsageRate(rateIntegerPart=10, ratePPR=10.0)
System.out.println(mapper.writeValueAsString(rate)); // it prints {"rate":10.0}

Upvotes: 1

Related Questions