Search code examples
javajsonbuilderunits-of-measurement

Serialize a javax.measure.Quantity property


If I try to serialize an Object as JSON and send it to a RESTful web service with a Quantity property != null, I get the following error

Caused by: java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->tec.units.ri.Identity["conversionSteps"]->java.util.ArrayList[0]->tec.units.ri.Identity["conversionSteps"]-> ... (and so on))
    at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.loadHttpMethod(ApacheHttpClient4Engine.java:430)
    at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:281)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->tec.units.ri.Identity["conversionSteps"]->java.util.ArrayList[0]->tec.units.ri.Identity["conversionSteps"]->java.util.ArrayList[0]->tec.units.ri.Identity["conversionSteps"]-> ... (and so on))
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:694)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:672)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:678)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
    ...
(and so on)

That's the simplified entity ...

import javax.measure.Quantity;
import javax.measure.quantity.Mass;

public class Compound {

    private Quantity<Mass> regAmount;

    public Quantity<Mass> getRegAmount() {
        return regAmount;
    }

    public void setRegAmount(Quantity<Mass> regAmount) {
        this.regAmount = regAmount;
    }
}

This is an example how I set the value:

Compound compound = new Compound();
double value = 1;
Unit<Mass> unit = Units.KILOGRAM;
Quantity<Mass> quantity = Quantities.getQuantity(value, unit);
compound.setRegAmount(quantity);

and this where the error is thrown (works for all other 20 entities without a Quantity):

Builder builder = ClientBuilder.newClient()
    .target(webServiceUrl)
    .path(path)
    .request(MediaType.APPLICATION_JSON);

Entity<E> e = Entity.json(entity); // works
response = builder.put(e); // error if regAmount != null

Used technologies: Jackson, RestEasy, javax.measure.unit-api (ver. 1.0), tec.units.unit-ri (ver. 1.0.3)

Questions:

  1. Do you know what is the problem and how to resolve this issue?
  2. Is there a different way to set regAmount?
  3. Can you recommend different approaches or technologies?

Thanks a lot in advance!


Solution

  • I tried some other interface implementation but had no luck.

    I found a solution which is good enough for the purpose of a RESTful web service. Therefore, I used a custom Serializer which gives me value and unit of the Quantity and this is actually everything I need to reconstruct the Quantity on the client side.

    public class QuantitySerializer<Q extends Quantity<Q>> extends JsonSerializer<Quantity<Q>> {
    
        @Override
        public void serialize(Quantity<Q> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeStartObject();
            try {
               gen.writeNumberField("value", value.getValue().intValue());
               gen.writeStringField("unit", value.getUnit().toString());
            } catch (NullPointerException e) {}
            gen.writeEndObject();
        }
    }