Сохранение кодовых точек Юникода между преобразованиями ISO-8859-1 в UTF-8 в XML в Java
Обратите внимание, что это не тот вопрос, который упоминался выше, поскольку возможен выход XML для сохранения кодовых точек.
У меня есть XML-файл UTF-8, который я могу отправить через HTTP в другую систему, которую я не могу контролировать. По какой-то безумной причине он решает преобразовать его в ISO-8859-1, потеряв много символов Юникода и заменив их символом "?". Затем эта система отправляет кому-то еще этот преобразованный XML-документ.
Как в Java на отправляющей стороне я могу избежать любого произвольного XML с кодовыми точками, отличными от ASCII, чтобы они выдержали эту промежуточную систему и все еще могли правильно декодироваться конечной точкой?
A -(UTF-8)-> B -(ISO-8859-1)-> C (декодирование во внутреннее представление Unicode).
import java.text.Normalizer;
import java.text.Normalizer.Form;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
import org.apache.commons.lang3.text.translate.NumericEntityEscaper;
public class Test {
private static CharSequenceTranslator translator = StringEscapeUtils.ESCAPE_XML
.with(NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE));
public static void main(String[] args) {
String s = "<note>\n<to>Tove</to>\n<from>Jani</from>\n<heading>Reminder</heading>\n<body>Don't forget me this weekend!test☠ä</body>\n</note>";
String xmlEscapedS = xmlToRobustXml(s);
System.out.println(xmlEscapedS);
}
/**
* @param s
* @return
*/
public static String xmlToRobustXml(String s) {
s = Normalizer.normalize(s, Form.NFC);
String xmlEscapedS = translator.translate(s);
return xmlEscapedS;
}
}
Я попробовал это, но это ускользает от всего.
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!test☠ä</body>
</note>
2 ответа
Вот три стандартных метода API для создания документов в кодировке ISO-8859-1.
Использование StAX API:
// output stream
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
// transcode
StringReader xml = new StringReader("<x>pi: \u03A0</x>");
XMLEventReader reader = XMLInputFactory.newFactory().createXMLEventReader(
xml);
XMLEventWriter writer = XMLOutputFactory.newFactory().createXMLEventWriter(
buffer, "ISO-8859-1");
try {
writer.add(reader);
} finally {
writer.close();
}
// proof
String decoded = new String(buffer.toByteArray(),
Charset.forName("ISO-8859-1"));
System.out.println(decoded);
Использование DOM API:
// output stream
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
// create XML DOM
InputSource src = new InputSource(new StringReader("<x>pi: \u03A0</x>"));
Document doc = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(src);
// serialize
DOMImplementationLS impl = (DOMImplementationLS) doc.getImplementation();
LSOutput out = impl.createLSOutput();
out.setEncoding("ISO-8859-1");
out.setByteStream(buffer);
impl.createLSSerializer().write(doc, out);
// proof
String decoded = new String(buffer.toByteArray(),
Charset.forName("ISO-8859-1"));
System.out.println(decoded);
Использование пакета transform:
// output stream
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
// transformation
StreamSource src = new StreamSource(new StringReader("<x>pi: \u03A0</x>"));
StreamResult res = new StreamResult(buffer);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
transformer.transform(src, res);
// proof
String decoded = new String(buffer.toByteArray(),
Charset.forName("ISO-8859-1"));
System.out.println(decoded);
Какой вы будете использовать, зависит от вашего варианта использования; StAX API, вероятно, самый эффективный.
Весь этот пример кода будет выдавать документы, эквивалентные:
<?xml version="1.0"?><x>pi: Π</x>
Кодовые точки Unicode выше 127 могут быть закодированы как числовые объекты, такие как {
используя следующее:
Из Apache Commons StringEscapeUtils. Прочитайте Javadoc, по умолчанию escapeXML
не конвертируется в числовые объекты.
StringEscapeUtils.ESCAPE_XML
.with(NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE));
Кстати, вы также пытаетесь отправить оригинальный XML с помощью заголовка Content-Type: application/x-xml
Итак, бинарный перевод.