XML 代理对编组
XML marshalling of surrogate pairs
当涉及代理对时,我遇到了 marshaller 的奇怪行为。为什么 JAXB 编组器添加不必要的(无效的)XML 实体?当我尝试整理以下内容时:
- \uD83D\uDCB3,例如55357 56499 个代码点
Mashaller 输出 128179 代码点(有效并代表 XML 中的代理对)和不必要的 56499(这不是有效的 XML 实体并代表对的低部分)。 如何配置编组器以在输出 中实现有效的 XML 实体,或者我是否只需要升级库?我正在使用 Java 8.
示例重现代码:
String inputSurrogate = "\uD83D\uDCB3";
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter sw = new StringWriter();
Customer customer = new Customer();
customer.setText(inputSurrogate);
jaxbMarshaller.marshal(customer, sw);
String xmlString = sw.toString();
System.out.println(xmlString);
for (int i = 0; i < xmlString.length(); i++) {
int ch = xmlString.codePointAt(i);
System.out.print(ch);
System.out.print("|");
}
输出(注意 |128179|56499|,56499 在我看来是不必要且无效的):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<text></text>
</customer>
60|63|120|109|108|32|118|101|114|115|105|111|110|61|34|49|46|48|34|32|101|110|99 |111|100|105|110|103|61|34|85|84|70|45|56|34|32|115|116|97|110|100|97|108|111|110|101|61 |34|121|101|115|34|63|62|10|60|99|117|115|116|111|109|101|114|62|10|32|32|32|32|60|116 |101|120|116|62|128179|56499|60|47|116|101|120|116|62|10|60|47|99|117|115|116|111|109|101|114|62 |10|
输出中不需要的 “代码点” 是输出代码中错误的产物。
Java 字符串有一个偏向 UTF-16 的接口。使用 char 数据类型的所有偏移量和所有方法都假装该字符串是一个 UTF-16 代码单元数组。
像"\uD83D\uDCB3"
这样的字符串转义也是如此。它不包含两个 Unicode 代码点。相反,它包含两个 UTF-16 代码单元,它们共同构成一个代码点,即信用卡符号的代码点。
您的输出代码通过使用 codePointAt()
访问代码点但按代码单元增加偏移量(变量 i
)来混合代码点和 UTF-16 代码单元。因此,信用卡代码点被访问了两次:一次正确,第二次错误(偏移量指向代理对的中间)。
正确的代码如下所示:
int offset = 0;
while (offset < xmlString.length()) {
int codePoint = xmlString.codePointAt(offset);
System.out.print(codePoint);
System.out.print("|");
offset += Character.charCount(codePoint);
}
当涉及代理对时,我遇到了 marshaller 的奇怪行为。为什么 JAXB 编组器添加不必要的(无效的)XML 实体?当我尝试整理以下内容时:
- \uD83D\uDCB3,例如55357 56499 个代码点
Mashaller 输出 128179 代码点(有效并代表 XML 中的代理对)和不必要的 56499(这不是有效的 XML 实体并代表对的低部分)。 如何配置编组器以在输出 中实现有效的 XML 实体,或者我是否只需要升级库?我正在使用 Java 8.
示例重现代码:
String inputSurrogate = "\uD83D\uDCB3";
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter sw = new StringWriter();
Customer customer = new Customer();
customer.setText(inputSurrogate);
jaxbMarshaller.marshal(customer, sw);
String xmlString = sw.toString();
System.out.println(xmlString);
for (int i = 0; i < xmlString.length(); i++) {
int ch = xmlString.codePointAt(i);
System.out.print(ch);
System.out.print("|");
}
输出(注意 |128179|56499|,56499 在我看来是不必要且无效的):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<text></text>
</customer>
60|63|120|109|108|32|118|101|114|115|105|111|110|61|34|49|46|48|34|32|101|110|99 |111|100|105|110|103|61|34|85|84|70|45|56|34|32|115|116|97|110|100|97|108|111|110|101|61 |34|121|101|115|34|63|62|10|60|99|117|115|116|111|109|101|114|62|10|32|32|32|32|60|116 |101|120|116|62|128179|56499|60|47|116|101|120|116|62|10|60|47|99|117|115|116|111|109|101|114|62 |10|
输出中不需要的 “代码点” 是输出代码中错误的产物。
Java 字符串有一个偏向 UTF-16 的接口。使用 char 数据类型的所有偏移量和所有方法都假装该字符串是一个 UTF-16 代码单元数组。
像"\uD83D\uDCB3"
这样的字符串转义也是如此。它不包含两个 Unicode 代码点。相反,它包含两个 UTF-16 代码单元,它们共同构成一个代码点,即信用卡符号的代码点。
您的输出代码通过使用 codePointAt()
访问代码点但按代码单元增加偏移量(变量 i
)来混合代码点和 UTF-16 代码单元。因此,信用卡代码点被访问了两次:一次正确,第二次错误(偏移量指向代理对的中间)。
正确的代码如下所示:
int offset = 0;
while (offset < xmlString.length()) {
int codePoint = xmlString.codePointAt(offset);
System.out.print(codePoint);
System.out.print("|");
offset += Character.charCount(codePoint);
}