使用MOXy 2.5.1快速且有点脏的JSON模式生成

因此,这些天我正在为即将推出的Oracle云服务开发新的REST API,因此我需要做的一件事情就是能够为模型中的bean自动生成JSON模式。 我正在使用MOXy从POJO生成JSON,从EclipseLink 2.5.1版本开始,它现在具有从bean模型生成JSON模式的能力。

将来会有一个更加正式的解决方案集成到Jersey 2.x; 但是如果您想尝试一下,此解决方案目前就可以使用。

因此,我们需要设置的第一类是模型处理器,这是非常内部的Jersey类,它允许我们使用额外的方法和资源来修改资源模型。 对于模型中的每个资源,我们都可以添加JsonSchemaHandler来完成生成新模式的工作。 由于这是一个简单的POC,因此这里没有缓存,如果要在生产代码中使用它,请注意这一点。

import com.google.common.collect.Lists;

import example.Bean;

import java.io.IOException;
import java.io.StringWriter;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.List;

import javax.inject.Inject;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import javax.xml.bind.JAXBException;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;

import org.eclipse.persistence.jaxb.JAXBContext;

import org.glassfish.jersey.process.Inflector;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.glassfish.jersey.server.model.ModelProcessor;
import org.glassfish.jersey.server.model.ResourceMethod;
import org.glassfish.jersey.server.model.ResourceModel;
import org.glassfish.jersey.server.model.RuntimeResource;
import org.glassfish.jersey.server.model.internal.ModelProcessorUtil;
import org.glassfish.jersey.server.wadl.internal.WadlResource;

public class JsonSchemaModelProcessor implements ModelProcessor {

  private static final MediaType JSON_SCHEMA_TYPE = 
    MediaType.valueOf("application/schema+json");
  private final List<ModelProcessorUtil.Method> methodList;


  public JsonSchemaModelProcessor() {
    methodList = Lists.newArrayList();
    methodList.add(new ModelProcessorUtil.Method("$schema", HttpMethod.GET, 
      MediaType.WILDCARD_TYPE, JSON_SCHEMA_TYPE,
      JsonSchemaHandler.class));
  }

  @Override
  public ResourceModel processResourceModel(ResourceModel resourceModel, 
      Configuration configuration) {
    return ModelProcessorUtil.enhanceResourceModel(resourceModel, true, methodList, 
      true).build();
  }

  @Override
  public ResourceModel processSubResource(ResourceModel resourceModel, 
      Configuration configuration) {
    return ModelProcessorUtil.enhanceResourceModel(resourceModel, true, methodList, 
      true).build();
  }


  public static class JsonSchemaHandler 
    implements Inflector<ContainerRequestContext, Response> {

    private final String lastModified = new SimpleDateFormat(WadlResource.HTTPDATEFORMAT).format(new Date());

    @Inject
    private ExtendedUriInfo extendedUriInfo;

    @Override
    public Response apply(ContainerRequestContext containerRequestContext) {

      // Find the resource that we are decorating, then work out the
      // return type on the first GET

      List<RuntimeResource> ms = extendedUriInfo.getMatchedRuntimeResources();
      List<ResourceMethod> rms = ms.get(1).getResourceMethods();
      Class responseType = null;
      found:
      for (ResourceMethod rm : rms) {
        if ("GET".equals(rm.getHttpMethod())) {
          responseType = (Class) rm.getInvocable().getResponseType();
          break found;
        }
      }

      if (responseType == null) {
        throw new WebApplicationException("Cannot resolve type for schema generation");
      }

      //
      try {
        JAXBContext context = (JAXBContext) JAXBContext.newInstance(responseType);

        StringWriter sw = new StringWriter();
        final StreamResult sr = new StreamResult(sw);

        context.generateJsonSchema(new SchemaOutputResolver() {
          @Override
          public Result createOutput(String namespaceUri, String suggestedFileName) 
              throws IOException {
            return sr;
          }
        }, responseType);


        return Response.ok().type(JSON_SCHEMA_TYPE)
          .header("Last-modified", lastModified)
          .entity(sw.toString()).build();
      } catch (JAXBException jaxb) {
        throw new WebApplicationException(jaxb);
      }
    }
  }


}

请注意, JsonSchemaHandler代码中的非常简单的启发式方法假定每个资源都有1:1映射到单个JSON Schema元素。 当然,这可能不适用于您的特定应用程序。

现在,我们已经在一个已知的位置生成了架构,我们需要将其告知客户,我们要做的第一件事是确保当用户在特定资源上调用OPTIONS时,有一个合适的链接头:

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.UriInfo;

public class JsonSchemaResponseFilter implements ContainerResponseFilter {

  @Context
  private UriInfo uriInfo;

  @Override
  public void filter(ContainerRequestContext containerRequestContext,
                     ContainerResponseContext containerResponseContext) throws IOException {


    String method = containerRequestContext.getMethod();
    if ("OPTIONS".equals(method)) {

      Link schemaUriLink =
        Link.fromUriBuilder(uriInfo.getRequestUriBuilder()
          .path("$schema")).rel("describedBy").build();

      containerResponseContext.getHeaders().add("Link", schemaUriLink);
    }
  }
}

由于这是JAX-RS 2.x,因此我们正在与之合作,将所有内容捆绑为一个功能:

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;

public class JsonSchemaFeature implements Feature {

  @Override
  public boolean configure(FeatureContext featureContext) {

    if (!featureContext.getConfiguration().isRegistered(JsonSchemaModelProcessor.class)) {
      featureContext.register(JsonSchemaModelProcessor.class);
      featureContext.register(JsonSchemaResponseFilter.class);
      return true;
    }
    return false;
  }
}

我不会展示我的整个POJO类集; 但是很快,这就是模式生成代码所需的带有@GET方法的Resource类:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/bean")
public class BeanResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Bean getBean() {
        return new Bean();
    }
}

最后,如果对资源执行GET,将看到以下内容:

GET .../resources/bean
Content-Type: application/json

{
  "message" : "hello",
  "other" : {
    "message" : "OtherBean"
  },
  "strings" : [
    "one",
    "two",
    "three",
    "four"
  ]
}

和选项:

OPTIONS .../resources/bean
Content-Type: text/plain
Link: <http://.../resources/bean/$schema>; rel="describedBy"

GET, OPTIONS, HEAD

最后,如果您解析架构资源:

GET .../resources/bean/$schema
Content-Type: application/schema+json

{
  "$schema" : "http://json-schema.org/draft-04/schema#",
  "title" : "example.Bean",
  "type" : "object",
  "properties" : {
    "message" : {
      "type" : "string"
    },
    "other" : {
      "$ref" : "#/definitions/OtherBean"
    },
    "strings" : {
      "type" : "array",
      "items" : {
        "type" : "string"
      }
    }
  },
  "additionalProperties" : false,
  "definitions" : {
    "OtherBean" : {
      "type" : "object",
      "properties" : {
        "message" : {
          "type" : "string"
        }
      },
      "additionalProperties" : false
    }
  }
}

这里有很多工作要做,特别是根据我不久前转发到Jersey 2.xa的声明性链接注释生成超媒体扩展。 但这确实指向解决方案,我们可以运用各种解决方案使某些事情现在起作用。

翻译自: https://www.javacodegeeks.com/2014/04/quick-and-a-bit-dirty-json-schema-generation-with-moxy-2-5-1.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值