最新的Open JDK 8 JAXB库无法解析包含新行字符

时间:2018-02-04 01:54:13

标签: java xml oracle jaxb unmarshalling

我在Ubuntu 16.04上使用Java。最近我升级到Open JDK java版本" 1.8.0_161"使用oracle-java8-installer软件包安装(软件包版本8u161-1~webupd8~0)。自从进行此升级以来,在进行Java对象的JAXB编组时,我遇到了新的异常。

具体来说,当尝试使用JAXB将Java对象编组为XML时,如果Java对象具有包含任何换行符(" \ n")和String属性的String属性,则会出现以下异常:被序列化为XML中的元素内容。 (另外,如果将String属性序列化为属性内容,则String值中的任何换行符都将转换为空格字符,并且不会触发异常。)

似乎正在发生的是

com.sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput $ NewLineEscapeHandler.escape

将Java对象的String属性中的换行符转换为实体引用
。然后将此实体引用写入XML输出流,但在验证实体引用名称时,将抛出异常,因为#xa未被识别为有效的实体引用名称。

这是预期的行为吗?如果是这样,我该怎么做才能在Java对象的序列化中保留换行符?如果没有,我该怎么做才能解决这个问题?

堆栈跟踪的相关部分是:

... Caused by: javax.xml.stream.XMLStreamException: Invalid name start character '#' (code 35) (name "#xa")
at com.fasterxml.aalto.out.XmlWriter.throwOutputError(XmlWriter.java:472)
at com.fasterxml.aalto.out.XmlWriter.reportNwfName(XmlWriter.java:383)
at com.fasterxml.aalto.out.ByteXmlWriter.verifyNameComponent(ByteXmlWriter.java:235)
at com.fasterxml.aalto.out.ByteXmlWriter.constructName(ByteXmlWriter.java:181)
at com.fasterxml.aalto.out.WNameTable.findSymbol(WNameTable.java:324)
at com.fasterxml.aalto.out.StreamWriterBase.writeEntityRef(StreamWriterBase.java:615)
at net.galexy.fieldguide.jaxb.CustomXMLStreamWriter.writeEntityRef(CustomXMLStreamWriter.java:198)
at com.sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput$XmlStreamOutWriterAdapter.writeEntityRef(XMLStreamWriterOutput.java:277)
at com.sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput$NewLineEscapeHandler.escape(XMLStreamWriterOutput.java:242)
... 60 more

例如,如果我解组以下XML:

<?xml version='1.0' encoding='UTF-8'?>
<description>
   <note>The text of the note</note>
</description>

然后尝试将其编组回XML,然后不会抛出任何异常。

但是,如果在音符内容中间有一个新行:

<?xml version='1.0' encoding='UTF-8'?>
<description>
   <note>The text of
         the note</note>
</description>

然后抛出异常。

正在使用的JAXB上下文是com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl

正在使用的JAXB编组器是com.sun.xml.internal.bind.v2.runtime.MarshallerImpl

在寻找有关这些更改的更多信息时,我发现了以下错误报告,该报告表明其他人在此版本的JAXB中遇到了相同的更改:

JDK-8196491 Newlines in JAXB string values of SOAP-requests are escaped to "&#xa;"

this stack overflow question的答案表明,我可以通过让我的编组人员使​​用com.sun.xml.bind.marshaller.CharacterEscapeHandler的自定义实现来恢复对角色转义的控制。

这令我感到困惑,因为javax.xml.bind.Marshaller似乎没有声明静态属性名称com.sun.xml.bind.marshaller.CharacterEscapeHandler,而它确实声明了其他属性名称,如Marshaller.JAXB_FORMATTED_OUTPUT,等于"jaxb.formatted.output。< / p>

即使我可以指示编组人员使​​用我的自定义字符转义处理程序,我也不完全确定我应该在该转义处理程序中做什么。是否有一个适当的基本转义处理程序,我可以覆盖它来继承所有标准转义处理,确保我介入以阻止换行字符的转义?

我也尝试过Oracle Java 9(软件包版本9.0.4-1~webupd8~0),该版本的Java也有同样的问题。

我还尝试了下一版本的Oracle Java 8(1.8.0_162),该版本也有同样的问题。

从Oracle网站(1.8.0_152)下载旧版本的Java可以解决问题,但不能解决问题。

2 个答案:

答案 0 :(得分:2)

杰夫S,

我试图对现有帖子发表评论,但我很快发现你需要“50个声誉”,而我却没有。

当我们迁移到JDK 1.8.0_161和1.8.0_162时,我遇到了类似的问题,我们的一些SOAP服务开始抛出以下异常

Feb 28, 2018 8:34:12 AM com.sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl createEntityReference
SEVERE: SAAJ0543: Entity References are not allowed in SOAP documents
SEVERE: java.lang.UnsupportedOperationException: Entity References are not allowed in SOAP documents
javax.xml.ws.WebServiceException: java.lang.UnsupportedOperationException: Entity References are not allowed in SOAP documents
    at com.sun.xml.internal.ws.handler.ClientSOAPHandlerTube.callHandlersOnRequest(ClientSOAPHandlerTube.java:135)
    at com.sun.xml.internal.ws.handler.HandlerTube.processRequest(HandlerTube.java:112)
    at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.java:1121)
    at com.sun.xml.internal.ws.api.pipe.Fiber._doRun(Fiber.java:1035)
    at com.sun.xml.internal.ws.api.pipe.Fiber.doRun(Fiber.java:1004)
    at com.sun.xml.internal.ws.api.pipe.Fiber.runSync(Fiber.java:862)
    at com.sun.xml.internal.ws.client.Stub.process(Stub.java:448)
    at com.sun.xml.internal.ws.client.sei.SEIStub.doProcess(SEIStub.java:178)
    at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:93)
    at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77)
    at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147)
    at com.sun.proxy.$Proxy38.getUserProfile(Unknown Source)

如上述问题和其他线索所示:

它与有效负载中的换行有关。例如,我们的一些有效负载包括具有导致问题的新行的XML字符串。但是如果在调用服务之前删除了换行符,那么它就可以工作了。见下文:

<强>故障

<?xml version="1.0" encoding="UTF-8"?>
<user>
<userId>XXXX</userId>
<name>XXXXXX, XXXXXX</name>
<phone>(xxx)xxx-xxxx</phone>
<title><![CDATA[MY TITLE]]></title>
<mail>xxx@xxxx.com</mail>
</user>

<强>作品

<?xml version="1.0" encoding="UTF-8"?><user><userId>XXXX</userId><name>XXXXXX, XXXXXX</name><phone>(xxx)xxx-xxxx</phone><title><![CDATA[MY TITLE]]></title><mail>xxx@xxxx.com</mail></user>

除了从“新行”剥离有效负载之外,您或其他任何人都知道是否存在解决方法,这是否是最新Oracle JDK中的错误,是否有任何计划可以纠正此行为。

由于

最大

答案 1 :(得分:2)

就我而言,我使用JAXB将一些对象转换为XML并通过StAX / WoodStox将它们序列化为文件。我已经设法通过过滤正在序列化的XML来解决问题。详细说,方法如下:

1)定义自定义StreamWriter2Delegate,覆盖writeEntityRef(),这样,当此方法收到错误的实体代码(#xd#xa)时,它会调用其委托实际上回写原始字符(即\n\r),实际上并不需要转义:

@Override
public void writeEntityRef ( String eref ) throws XMLStreamException
{
    if ( eref == null || !eref.startsWith ( "#x" ) ) {
        super.writeEntityRef ( eref );
        return;
    }
    String hex = eref.substring ( 2 );
    for ( char c: new char[] { '\r', '\n' } )
        if ( Integer.toHexString ( c ).equals ( hex ) ) {
            this.writeCharacters ( Character.toString ( c ) );
            return;
    }
    super.writeEntityRef ( eref );
}

对于此问题,这与fix they've already filed相当(除了一些重读),JDK8u192应该可以使用(并且应该已经在JDK 9/10中)。

2)用上述过滤器包裹你的XMLStreamWriter2,例如:

FileOutputStream fout = new FileOutputStream ( "test.xml" );
WstxOutputFactory wsof = (WstxOutputFactory) WstxOutputFactory.newInstance();
XMLStreamWriter2 xmlOut = (XMLStreamWriter2) wsof.createXMLStreamWriter ( fout, CharsetNames.CS_UTF8 );
xmlOut = new NewLineFixWriterFilter ( xmlOut );
// Now write into xmlOut, directly or via JAXB

完整/生产代码为here。将相同的方法适用于类似的管道并不困难(一般来说,问题出现是因为com.sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput以错误的方式逃避\n\r,所以诀窍是从上层劫持这个错误的编码。)