我在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 "
"
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可以解决问题,但不能解决问题。
答案 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
,所以诀窍是从上层劫持这个错误的编码。)