我有两个应用程序必须协同工作。一个是我使用 Java 17 自己构建的应用程序,另一个是我无法控制的应用程序,一个使用 Java 8。
我的 Java 17 应用程序必须采用 POJO 并将其编组为 XML,然后通过 SOAP 将其发送到 Java 8 应用程序。然后,Java 8 应用程序接收此 XML 并使用 Castor 将其解组为 POJO,然后对其进行处理。
但是,对于外来(非 ASCII)字符,所有这一切都会崩溃。我已确保两侧的编码都设置为 UTF-8 在 Java 17 应用程序中,我使用 Jakarta/JAXB 来编组到 XML。
JAXBContext context = JAXBContext.newInstance(PojoClass.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
//marshaller.setProperty("org.glassfish.jaxb.characterEscapeHandler", new NonAsciiEscapeHandler());
StringWriter sw = new StringWriter();
marshaller.marshal(pojoClassInstance, sw);
return sw.toString();
我尝试添加一个单独的字符转义处理程序以将所有外来字符转换为其十六进制值。据我了解,如果 XML 中的非 ASCII 字符是十六进制,则不需要实体声明。
然而,在 Java 8 应用程序中,Castor 对其进行解组,Breeze 随后解析 XML。但不幸的是,结果,我在 com.tbf.xml.XmlObjectFactory 中收到错误:“引用了实体‘Otilde’,但未声明。”是的,我知道这意味着我需要将其声明为 XML 实体,但是 为什么当我显式发送十六进制值时它会这样做??? 或者如果我不发送十六进制值,我会发送UTF-8 编码的外来字符不应该随机转换为实体。 有人可以帮助我吗:1.使用 Castor 获取 Java 8 应用程序以接受 UTF-8 或十六进制值,并且不抱怨未声明的实体,或者 2.使用 Java 17 中的 JAXB 编组器在生成的生成中添加实体声明XML?谢谢!
编辑:收到评论后,这是在上面的代码之后将外来字符转换为十六进制后出现的示例(使用 NonAsciiCharacterHandler 运行,如未注释掉)。
<PostalAddr>
<AddrLine1>Õ888 Sample Rd.</AddrLine1>
<AddrLine2>È021 Apartment 55</AddrLine2>
<CityNm>óDoyle</CityNm>
<PostalZIPCd>99999</PostalZIPCd>
<StateProvCd>AK</StateProvCd>
</PostalAddr>
然后将其转换为 SOAP 对象并通过 Apache HttpClient 发送。
try(CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(url);
HttpEntity entity = new StringEntity(xmlMessage, StandardCharsets.UTF_8);
httpPost.setEntity(entity);
httpPost.setHeader("Content-type", "application/soap+xml");
httpPost.setHeader("Accept", "application/soap+xml");
httpPost.addHeader("Accept-Charset", "utf-8");
httpPost.setHeader("SOAPAction", "SOAP");
CloseableHttpResponse response = client.execute(httpPost);
但是,我在此处包含了一个示例,以了解当我在同一个 Java 17 应用程序中获取 HttpEntity 并提取它时会发生什么。
HttpEntity entity2 = httpPost.getEntity();
String entityContents = EntityUtils.toString(entity2, StandardCharsets.UTF_8);
当我这样做时,XML 显示如下:
<PostalAddr>
<AddrLine1>Õ888 Sample Rd.</AddrLine1>
<AddrLine2>È021 Apartment 55</AddrLine2>
<CityNm>óDoyle</CityNm>
<PostalZIPCd>99999</PostalZIPCd>
<StateProvCd>AK</StateProvCd>
</PostalAddr>
当使用 Castor 在 Java 8 中解组时,会出现以下情况:
<PostalAddr>
<AddrLine1>Õ888 Sample Rd.</AddrLine1>
<AddrLine2>È021 Apartment 55</AddrLine2>
<CityNm>óDoyle</CityNm>
<PostalZIPCd>99999</PostalZIPCd>
<StateProvCd>AK</StateProvCd>
</PostalAddr>
当使用 Breeze 解析它时,这会导致问题。
但无论如何,我相信 Castor 的解组是造成这种差异的原因。我需要 Castor 不要像这样解组它,并且实际上使用十六进制代码或实际字符进行解组。
但是,您必须确保使用正确的内容类型正确传输 HTTP 正文,例如
Content-Type: application/xml+soap; charset=utf-8
。请注意,默认的 HTTP 内容编码是 ISO-8859-1(至少这是我上次查看的时候)。
escapaJava
(
StringEscapeUtils
) [或 (org.apache.commons.text
),如果您使用的是旧版本] 中的 org.apache.commons.lang3
方法,并在收到时调用 unescapaJava
方法.如果仍然不起作用,除了转义/取消转义之外,您可能还想使用 ByteArrayEntity
代替
StringEntity
。HttpEntity entity = new ByteArrayEntity(StringUtils.escapeJava(xmlMessage).getBytes(), ContentType.APPLICATION_SOAP_XML);
然后以同样的方式读取它,并在将其转换回字符串后取消转义。
您可能需要尝试不同的发送/接收方式才能使其正常工作。