使用外来字符编组 XML 需要实体声明

问题描述 投票:0回答:1

我有两个应用程序必须协同工作。一个是我使用 Java 17 自己构建的应用程序,另一个是我无法控制的应用程序,一个使用 Java 8。

我的 Java 17 应用程序必须采用 POJO 并将其编组为 XML,然后通过 SOAP 将其发送到 Java 8 应用程序。然后,Java 8 应用程序接收此 XML 并将其解组为 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 17 应用程序)中解组 XML,它解组后显示实际的外来字符而不是十六进制值。

在 Java 8 应用程序中,在解组时,它将再次显示实际的外来字符而不是十六进制值。然而,在 Java 8 应用程序中,Breeze 负责解析 XML。但不幸的是,结果,我在 com.tbf.xml.XmlObjectFactory 中收到错误:“引用了实体‘Otilde’,但未声明。”是的,我知道这意味着我需要将其声明为 XML 实体,但是 为什么当我显式发送十六进制值时它会这样做???

有人可以帮助我吗:1. 让 Java 8 应用程序接受十六进制值而不抱怨未声明的实体,或者 2. 使用 Java 17 中的 JAXB 编组器在生成的 XML 中添加实体声明?谢谢!

编辑:收到评论后,这是在上面的代码之后将外来字符转换为十六进制后出现的示例(使用 NonAsciiCharacterHandler 运行,如未注释掉)。

<PostalAddr>
    <AddrLine1>&#xd5;888 Sample Rd.</AddrLine1>
    <AddrLine2>&#xc8;021 Apartment 55</AddrLine2>
    <CityNm>&#xf3;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>

同样,这也出现在 Java 8 应用程序中。不知何故,十六进制值被转换为实际值,Java 8 应用程序中的 Breeze 将其解释为实体“Otilde”等,从而抛出实体声明丢失的错误。

java xml soap jaxb unmarshalling
1个回答
0
投票

如果发送方和接收方都接受 UTF-8,那么您不必处理转义非 ascii 字符,也不必使用任何实体声明。

但是,您必须确保使用正确的内容类型正确传输 HTTP 正文,例如

Content-Type: application/xml+soap; charset=utf-8
。请注意,默认的 HTTP 内容编码是 ISO-8859-1(至少这是我上次查看的时候)。

© www.soinside.com 2019 - 2024. All rights reserved.