xs:int unmarshalled to null for decimal values
我遇到了一个与基于 JAX-WS 的 WebService 中的解组过程相关的问题。在 WSDL 文件中有一个元素定义为
1 | <element name="quantity" nillable="true" type="int" /> |
在相关的JAVA类中定义为:
1 2 | @XmlElement(name ="Quantity", required = true, type = Integer.class, nillable = true) protected Integer quantity; |
当此元素的 XML 值是十进制数 (3.4) 的表示形式时,该元素将被解组为空整数。不会生成 SOAPFault,并且无法在 WebService 中区分十进制值和空值。
这可能是 JAXB 实现中的缺陷还是我做错了什么?
Could it be a defect in JAXB implementation or I'm doing something
wrong?
这不是 JAXB (JSR-222) 实现中的缺陷。这是将 JAX-WS 配置为使用 JAXB 的结果。我将在下面用一个例子来演示。
根
下面是一个域对象,其字段与您问题中的字段相匹配。我已经从
1 2 3 4 5 6 7 8 9 10 | import javax.xml.bind.annotation.*; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Root { @XmlElement(name ="Quantity", required = true, nillable = true) protected Integer quantity; } |
演示
JAXB 提供了在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import java.io.StringReader; import javax.xml.bind.*; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Root.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); unmarshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { System.out.println(event.getMessage()); return true; } }); StringReader xml = new StringReader("<root><Quantity>3.4</Quantity></root>"); Root root = (Root) unmarshaller.unmarshal(xml); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root, System.out); } } |
输出
在专家组中,我们认为无效元素数据很常见,并且每次遇到这种情况时 JAXB 都不应失败,但您可以看到引发了
1 2 3 4 5 | Not a number: 3.4 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <root> <Quantity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/> </root> |
更新演示
如果我们更新
1 2 3 4 5 | @Override public boolean handleEvent(ValidationEvent event) { System.out.println(event.getMessage()); return false; } |
更新的输出
现在出现以下输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Not a number: 3.4 Exception in thread"main" javax.xml.bind.UnmarshalException: Not a number: 3.4 - with linked exception: [java.lang.NumberFormatException: Not a number: 3.4] at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:676) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:672) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.handleParseConversionException(Loader.java:256) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:54) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.text(UnmarshallingContext.java:499) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.processText(SAXConnector.java:166) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:139) at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:606) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1742) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2900) 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) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:175) at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157) at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:214) at forum14741140.Demo.main(Demo.java:22) Caused by: java.lang.NumberFormatException: Not a number: 3.4 at com.sun.xml.internal.bind.DatatypeConverterImpl._parseInt(DatatypeConverterImpl.java:101) at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:713) at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:711) at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.parse(TransducedAccessor.java:232) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:50) ... 19 more |
我回答了这个问题:https://stackoverflow.com/a/30617814/3632201
上周我一直在努力解决这个问题,最后我找到了一个可行的解决方案。诀窍是 JAXB 在使用 @XmlRootElement 注释的对象中查找方法 beforeUnmarshal 和 afterUnmarshal。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | .. @XmlRootElement(name="MSEPObtenerPolizaFechaDTO") @XmlAccessorType(XmlAccessType.FIELD) public class MSEPObtenerPolizaFechaDTO implements Serializable { .. public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException, IOException, SAXException { unmarshaller.setSchema(Utils.getSchemaFromContext(this.getClass())); unmarshaller.setEventHandler(new CustomEventHandler()); } public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException { unmarshaller.setSchema(null); unmarshaller.setEventHandler(null); } |
使用这个 ValidationEventHandler:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class CustomEventHandler implements ValidationEventHandler{ @Override public boolean handleEvent(ValidationEvent event) { if (event.getSeverity() == event.ERROR || event.getSeverity() == event.FATAL_ERROR) { ValidationEventLocator locator = event.getLocator(); throw new RuntimeException(event.getMessage(), event.getLinkedException()); } return true; } } |
}
这是在你的 Utility 类中创建的 getSchemaFromContext 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @SuppressWarnings("unchecked") public static Schema getSchemaFromContext(Class clazz) throws JAXBException, IOException, SAXException{ JAXBContext jc = JAXBContext.newInstance(clazz); final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>(); jc.generateSchema(new SchemaOutputResolver(){ @Override public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); outs.add(out); StreamResult streamResult = new StreamResult(out); streamResult.setSystemId(""); return streamResult; } }); StreamSource[] sources = new StreamSource[outs.size()]; for (int i = 0; i < outs.size(); i++) { ByteArrayOutputStream out = outs.get(i); sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()),""); } SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); return sf.newSchema(sources); } |