【问题标题】:Access JAX-RS resource annotations from a JsonbSerializer从 JsonbSerializer 访问 JAX-RS 资源注释
【发布时间】:2019-12-20 16:18:52
【问题描述】:

我有一个使用自定义 GSON JSON 适配器在 Payara 4 上运行的应用程序。我想迁移到 Payara 5 (5.191) 并开始使用 JSON-B。在我们当前的应用程序中,我们可以使用资源上的注释来控制 JSON 输出。

例如使用@Summarize:

@GET
@Path("summary/{encryptedId}")
@Produces(MediaType.APPLICATION_JSON)
@Summarize
public Address findSummarized(@PathParam("encryptedId") String encryptedId) {
  return super.find(encryptedId);
}

这将导致在我们的@Provider 中使用不同的 GSON 配置:

@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class GsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {

  public GsonProvider() {
    gson = getGson(EntityAdapter.class);
    gsonSummary = getGson(EntitySummaryAdapter.class);
  }

  ...

  @Override
  public void writeTo(T object,
                      Class<?> type,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mediaType,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream entityStream)
  throws IOException, WebApplicationException {
    boolean summarize = contains(annotations, Summarize.class);
    try (PrintWriter printWriter = new PrintWriter(entityStream)) {
      printWriter.write((summarize ? gsonSummary : gson).toJson(object));
      printWriter.flush();
    }
  }

}

我想在新的 JSON-B 设置中做类似的事情。我用@JsonbTypeSerializer(MySerializer.class) 注释了我们的实体,所以我希望能够从序列化程序中检测它应该做什么:要么创建一个完整的序列化 JSON 对象,要么创建一个摘要。

我希望做的是在JsonbConfig 中设置一个属性,如下所示:

JsonbConfig config = new JsonbConfig()
        .setProperty("com.myCompany.jsonb.summarize", true);

并使用@Context 在序列化程序中读取它(只是猜测这可能在这里工作),如下所示:

@Context
private JsonbConfiguration config;

.. 但事实并非如此。有没有办法从JsonbSerializer 访问 JAX-RS 资源注释?

【问题讨论】:

  • 是否可以创建一个AddressSummary 类来表示汇总数据,然后您可以拥有一个AddressSummary.from(Address) 静态创建者方法,而您的findSummarized JAX-RS 方法可以返回一个@987654334 @ 而不是 Address 对象
  • 我宁愿只有一两个(反)序列化器。我们的模型由近 200 个实体组成。但这可能是一种选择。

标签: jax-rs java-ee-8 jsonb-api


【解决方案1】:

您可以使用 JAX-RS 提供程序类中的两个单独的 Jsonb 实例来实现类似的目标,如下所示:

@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class JsonbProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {

  private static final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
                                       .withAdapters(new EntityAdapter()));
  private static final Jsonb jsonbSummary = JsonbBuilder.create(new JsonbConfig()
                                       .withAdapters(new EntitySummaryAdapter()));

  ...

  @Override
  public void writeTo(T object,
                      Class<?> type,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mediaType,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream entityStream)
  throws IOException, WebApplicationException {
    boolean summarize = contains(annotations, Summarize.class);
    try (PrintWriter printWriter = new PrintWriter(entityStream)) {
      printWriter.write((summarize ? jsonbSummary : jsonb).toJson(object));
      printWriter.flush();
    }
  }

}

【讨论】:

  • 这里的关键是使用JsonbAdapter。我喜欢这个概念。不幸的是,它不像您的答案那样工作(使用构建器withAdapters)。我必须用 @JsonbTypeAdapter(EntityAdapter) 注释我的实体才能使适配器正常工作......这只允许我使用一个适配器。
【解决方案2】:

最后,我选择从我的实体中创建摘要,并将注释放在我的 REST 资源上。这有点工作,但我认为这是值得的。

我创建了一个Summarizable 接口并在其中添加了一个默认方法来创建任何实体的简单地图摘要,基于我们为完整版实体创建的PropertyVisibilityStrategy 的扩展版本。

public interface Summarizable {

  public default Map<String, Object> toSummary() {
    SummaryPropertyVisibilityStrategy summaryStrategy = new SummaryPropertyVisibilityStrategy();
    Map<String, Object> summary = new LinkedHashMap<>();
    ReflectionUtils.getFields(this.getClass())
            .stream()
            .filter(summaryStrategy::isVisible)
            .map(f -> new AbstractMap.SimpleEntry<>(f.getName(), summarize(f)))
            .filter(e -> e.getValue() != null)
            .forEach(e -> summary.put(e.getKey(), e.getValue()));
    return summary;
  }

  public default Object summarize(final Field field) {
    Object value = ReflectionUtils.getValueJsonb(this, field);
    return value != null && Stream.of(ManyToOne.class, OneToOne.class).anyMatch(field::isAnnotationPresent)
                   ? value.toString()
                   : value;
  }

}
  public static Object getValueJsonb(final Object object, final Field field) {
    field.setAccessible(true);
    JsonbTypeAdapter adapterAnnotation = field.getAnnotation(JsonbTypeAdapter.class);
    try {
      Object value = field.get(object);
      return adapterAnnotation == null
             ? value
             : adapterAnnotation.value().newInstance().adaptToJson(value);
    }
    catch (Exception ex) {
      throw new IllegalStateException(ex);
    }
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多