4

When requesting a single com.codahale.metrics.Meter via Jersey-REST-Service, Jackson is doing just fine and serializes the Meter to (formatting done by me)

{
  "count":0,
  "fiveMinuteRate":0.0,
  "fifteenMinuteRate":0.0,
  "oneMinuteRate":0.0,
  "meanRate":0.0
}

When requesting the same Meter contained inside a Map<String, Metric>, Jackson complaines:

No serializer found for class com.codahale.metrics.Meter and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.HashMap["testMetric"])

I could just make my resource-method return the String that the ObjectMapper is able to create (see below), but I would like to keep my method returning Map<String, Metric>.

Why can't Jackson figure it out on its own? How can I make Jackson realize that it knows how to serialize the Object? How can I check the configuration of the ObjectMapper used by Jersey?

I have verified that the Map is not empty and that the Meter is acutally there.

When I create a vanilla ObjectMapper and serialize the Map

ObjectMapper mapper = new ObjectMapper();
mapper.writeValueAsString( myMetricMap );

it works just fine:

{
  "testMetric":
  {
    "count":0,
    "fiveMinuteRate":0.0,
    "fifteenMinuteRate":0.0,
    "oneMinuteRate":0.0,
    "meanRate":0.0
  }
}

dropwizard-metrics provides a metrics-json-Package that includes Serializers for Metrics and HealthChecks. Unfortunately, due to a dependency mess, I cannot use it.

EDIT: As it was suggested in the comments, I've tried to serialize a Map<String, Meter> - and it worked!

To summarize:

public interface Metric {
  // Empty Marker Interface
}

public class Meter implements Metric {
  ...
}

@Path("/")
@Produces( MediaType.APPLICATION_JSON )
public MetricsResource {
  @GET
  @Path("metric/{metricName}")
  public Metric getMetric( @PathParam String metricName ) {
    // Works!
    return MetricsService.getMetric( metricName );
  }

  @GET
  @Path("metricsAsString")
  public String getMetricsAsString()
  {
    // Works!
    return (new ObjectMapper()).writeValueAsString(
        MetricsService.getMetrics());
  }

  @GET
  @Path("meters")
  public Map<String, Meter> getMeters()
  {
    // Works!
    return MetricsService.getMeters();
  }

  @GET
  @Path("metrics")
  public Map<String, Metric> getMetrics()
  {
    // Fails!
    return metricsService.getMetrics();
}

I suppose that jackson is not determining the concrete Type when implicitly serializing, but is trying to serialize based on the (marker) Interface Metric.

p0nder
  • 41
  • 4
  • `Metric` and `Meter` are two different classes, try returning `Metric` class from your controller to see if it serializes correctly. – Hemant Patel Apr 17 '19 at 12:39
  • That's true. `Meter` ist implementing `Metric` though. I did what you asked and now it is serializing correctly. I was under the impression that Jackson will be able to determine the concrete type during runtime and serialize based on the concrete type. at least this is what it did when being called from the `ObjectMapper` and from the Single-Object-Resource. I Always return `Metric` and never explicitly `Meter`. – p0nder Apr 17 '19 at 13:22

0 Answers0