4

我目前正在将 jersey 1.x 项目迁移到 2.4.1,并且在使用枚举作为参数(PathParam、QueryParam 等)时出错。基本上,根据球衣对方法参数的第三个要求,这个枚举应该是有效的

有一个名为 valueOf 或 fromString 的静态方法,它接受单个 String 参数(例如,参见 Integer.valueOf(String) 和 java.util.UUID.fromString(String));

由于这个项目使用 xsd 作为合同来生成 java 类,我有:

<xs:simpleType name="status">
    <xs:restriction base="xs:string">
        <xs:enumeration value="ACTIVE" />
        <xs:enumeration value="INACTIVE" />
    </xs:restriction>
</xs:simpleType>

生成:

@XmlType(name = "status")
@XmlEnum
public enum Status {

    ACTIVE,
    INACTIVE;

    public String value() {
        return name();
    }

    public static Status fromValue(String v) {
        return valueOf(v);
    }

}

当它用于:

@GET
public Response search(@QueryParam("status") my.package.Status status) {
//..other code here
}

它产生:

org.glassfish.jersey.server.internal.inject.ExtractorException: Error unmarshalling JAXB object of type "class my.package.Status".
    at org.glassfish.jersey.server.internal.inject.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:195)
    at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.convert(AbstractParamValueExtractor.java:138)
    at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.fromString(AbstractParamValueExtractor.java:129)
    at org.glassfish.jersey.server.internal.inject.SingleValueExtractor.extract(SingleValueExtractor.java:83)
    at org.glassfish.jersey.server.internal.inject.QueryParamValueFactoryProvider$QueryParamValueFactory.provide(QueryParamValueFactoryProvider.java:88)
    at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:81)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:121)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:152)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:104)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:367)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:349)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:106)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:259)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:318)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:983)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:361)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:372)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:218)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:401)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:450)
    at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
    at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:928)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: javax.xml.bind.UnmarshalException
 - with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.]
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.createUnmarshalException(AbstractUnmarshallerImpl.java:335)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.createUnmarshalException(UnmarshallerImpl.java:512)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:209)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:181)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:232)
    at org.glassfish.jersey.server.internal.inject.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:192)
    ... 41 more
Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177)
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:441)
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:368)
    at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1388)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:998)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:116)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:203)
    ... 44 more

因为它是在 JaxbStringReaderProvider 解组期间触发的,所以当我生成的类没有 xml 注释时不应该是这种情况。在不为每个需要的枚举类型创建带有字符串构造函数的包装对象的情况下,处理枚举类型方法参数的最佳方法是什么?

4

2 回答 2

6

您应该实现自定义ParamConverterProviderParamConverter并将您的实现注册到 JAX-RS 运行时(使用 JAX-RSApplicationweb.xml)。就像是:

@Provider
public class GeneratedEnumParamConverterProvider implements ParamConverterProvider {

    @Override
    public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType,
                                              final Annotation[] annotations) {
        try {
            final Method fromValueMethod = rawType.getMethod("fromValue", String.class);

            return new ParamConverter<T>() {
                @Override
                public T fromString(final String value) {
                    try {
                        return rawType.cast(fromValueMethod.invoke(null, value));
                    } catch (Exception e) {
                        throw new IllegalArgumentException("Given value (" + value + ") cannot be converted to parameter of type" + rawType);
                    }
                }

                @Override
                public String toString(final T value) {
                    return value.toString();
                }
            };
        } catch (Exception e) {
            return null;
        }
    }
}
于 2013-11-14T16:06:45.583 回答
0

在 org.eclipse.persistence.jaxb.JAXBContext 它以这种方式发起 DEFAULT_VALIDATION_EVENT_HANDER :

 protected static final ValidationEventHandler DEFAULT_VALIDATION_EVENT_HANDER = new ValidationEventHandler() {
     public boolean handleEvent(ValidationEvent event) {
         return event.getSeverity() < ValidationEvent.FATAL_ERROR;
     }
 };

它将级别提高到 FATAL ERROR,我认为应该是 ERROR。所以上面的代码可以是:

 protected static final ValidationEventHandler DEFAULT_VALIDATION_EVENT_HANDER = new DefaultValidationEventHandler();

我在创建服务器时使用反射修改了DEFAULT_VALIDATION_EVENT_HANDER,然后遇到错误的枚举字符串值时会抛出异常。

在 JAXBContext 中修改 DEFAULT_VALIDATION_EVENT_HANDER 的代码:

    final Field field = JAXBContext.class.getDeclaredField("DEFAULT_VALIDATION_EVENT_HANDER");
    field.setAccessible(true);

    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, new DefaultValidationEventHandler());
于 2014-12-15T19:13:45.983 回答