From 3b4e77c96fd36086a16f84f12c76f66266911cda Mon Sep 17 00:00:00 2001 From: Nathan Reline Date: Thu, 26 Nov 2020 15:49:16 -0700 Subject: [PATCH] Support charsets other than UTF-8 --- .../main/java/com/tickaroo/tikxml/TikXml.java | 17 +++- .../com/tickaroo/tikxml/TikXmlConfig.java | 12 +++ .../java/com/tickaroo/tikxml/XmlReader.java | 67 +++++++++------ .../java/com/tickaroo/tikxml/XmlWriter.java | 84 +++++++++++-------- .../tickaroo/tikxml/TikXmlBuilderTest.java | 18 ++++ .../com/tickaroo/tikxml/XmlReaderTest.java | 11 +++ .../com/tickaroo/tikxml/XmlWriterTest.java | 80 +++++++++++++++++- core/src/test/resources/iso_8859_1.xml | 1 + 8 files changed, 222 insertions(+), 68 deletions(-) create mode 100644 core/src/test/resources/iso_8859_1.xml diff --git a/core/src/main/java/com/tickaroo/tikxml/TikXml.java b/core/src/main/java/com/tickaroo/tikxml/TikXml.java index 04b224c0..73253178 100644 --- a/core/src/main/java/com/tickaroo/tikxml/TikXml.java +++ b/core/src/main/java/com/tickaroo/tikxml/TikXml.java @@ -21,6 +21,8 @@ import com.tickaroo.tikxml.typeadapter.TypeAdapter; import java.io.IOException; import java.lang.reflect.Type; +import java.nio.charset.Charset; + import okio.BufferedSink; import okio.BufferedSource; @@ -64,6 +66,17 @@ public Builder writeDefaultXmlDeclaration(boolean writeDeclaration) { return this; } + /** + * Specify the charset + * + * @param charset character encoding set to use when reading and writing the xml document + * @return The Builder itself + */ + public Builder charset(Charset charset) { + config.charset = charset; + return this; + } + /** * Adds an type converter for the given class * @@ -105,7 +118,7 @@ private TikXml(TikXmlConfig config) { public T read(BufferedSource source, Type clazz) throws IOException { - XmlReader reader = XmlReader.of(source); + XmlReader reader = XmlReader.of(source, config.charset); reader.beginElement(); reader.nextElementName(); // We don't care about the name of the root tag @@ -125,7 +138,7 @@ public void write(BufferedSink sink, T valueToWrite) throws IOException { public void write(BufferedSink sink, T valueToWrite, Type typeOfValueToWrite) throws IOException { - XmlWriter writer = XmlWriter.of(sink); + XmlWriter writer = XmlWriter.of(sink, config.charset); TypeAdapter adapter = config.getTypeAdapter(typeOfValueToWrite); if (config.writeDefaultXmlDeclaration()) { diff --git a/core/src/main/java/com/tickaroo/tikxml/TikXmlConfig.java b/core/src/main/java/com/tickaroo/tikxml/TikXmlConfig.java index fca78108..0b7e72e7 100644 --- a/core/src/main/java/com/tickaroo/tikxml/TikXmlConfig.java +++ b/core/src/main/java/com/tickaroo/tikxml/TikXmlConfig.java @@ -21,6 +21,8 @@ import com.tickaroo.tikxml.typeadapter.TypeAdapter; import java.lang.reflect.Type; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Holds the config for parsing and writing xml via {@link TikXml} @@ -34,6 +36,7 @@ public final class TikXmlConfig { TypeConverters typeConverters = new TypeConverters(); TypeAdapters typeAdapters = new TypeAdapters(); boolean writeDefaultXmlDeclaration = true; + Charset charset = StandardCharsets.UTF_8; TikXmlConfig() { } @@ -58,6 +61,15 @@ public boolean writeDefaultXmlDeclaration() { return writeDefaultXmlDeclaration; } + /** + * The charset + * + * @return character encoding set to use when reading and writing the xml document + */ + public Charset charset() { + return charset; + } + /** * Query a {@link TypeConverter} for a given class * diff --git a/core/src/main/java/com/tickaroo/tikxml/XmlReader.java b/core/src/main/java/com/tickaroo/tikxml/XmlReader.java index 448fa2c9..a4f6d447 100644 --- a/core/src/main/java/com/tickaroo/tikxml/XmlReader.java +++ b/core/src/main/java/com/tickaroo/tikxml/XmlReader.java @@ -26,6 +26,8 @@ import java.io.Closeable; import java.io.EOFException; import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * A class to read and parse an xml stream. @@ -37,14 +39,13 @@ public class XmlReader implements Closeable { //private static final ByteString LINEFEED_OR_CARRIAGE_RETURN = ByteString.encodeUtf8("\n\r"); - private static final ByteString UNQUOTED_STRING_TERMINALS - = ByteString.encodeUtf8(" >/=\n"); + private final ByteString unquotedStringTerminals; - private static final ByteString CDATA_CLOSE = ByteString.encodeUtf8("]]>"); - private static final ByteString CDATA_OPEN = ByteString.encodeUtf8(""); - private static final ByteString XML_DECLARATION_CLOSE = ByteString.encodeUtf8("?>"); + private final ByteString cdataClose; + private final ByteString cdataOpen; + private final ByteString doctypeOpen; + private final ByteString commentClose; + private final ByteString xmlDeclarationClose; private static final ByteString UTF8_BOM = ByteString.of((byte) 0xEF, (byte) 0xBB, (byte) 0xBF); private static final byte DOUBLE_QUOTE = '"'; @@ -97,21 +98,33 @@ public class XmlReader implements Closeable { private final BufferedSource source; private final Buffer buffer; + private final Charset charset; private String currentElementName; - private XmlReader(BufferedSource source) { + private XmlReader(BufferedSource source, Charset charset) { if (source == null) { throw new NullPointerException("source == null"); } this.source = source; this.buffer = source.buffer(); + this.charset = charset; + unquotedStringTerminals = ByteString.encodeString(" >/=\n", charset); + cdataClose = ByteString.encodeString("]]>", charset); + cdataOpen = ByteString.encodeString("", charset); + xmlDeclarationClose = ByteString.encodeString("?>", charset); } /** * Returns a new instance that reads a XML-encoded stream from {@code source}. */ public static XmlReader of(BufferedSource source) { - return new XmlReader(source); + return new XmlReader(source, StandardCharsets.UTF_8); + } + + public static XmlReader of(BufferedSource source, Charset charset) { + return new XmlReader(source, charset); } /** @@ -313,7 +326,7 @@ private int doPeek() throws IOException { * @throws IOException */ private boolean isCDATA() throws IOException { - return fillBuffer(CDATA_OPEN.size()) && buffer.rangeEquals(0, CDATA_OPEN); + return fillBuffer(cdataOpen.size()) && buffer.rangeEquals(0, cdataOpen); } /** @@ -324,8 +337,8 @@ private boolean isCDATA() throws IOException { * @throws IOException */ private boolean isDocTypeDefinition() throws IOException { - return buffer.size() >= DOCTYPE_OPEN.size() && - buffer.snapshot(DOCTYPE_OPEN.size()).toAsciiUppercase().equals(DOCTYPE_OPEN); + return buffer.size() >= doctypeOpen.size() && + buffer.snapshot(doctypeOpen.size()).toAsciiUppercase().equals(doctypeOpen); } /** @@ -564,14 +577,14 @@ public String nextTextContent() throws IOException { + "> but haven't found"); } - return buffer.readUtf8(index); + return buffer.readString(index, charset); } else if (p == PEEKED_CDATA) { peeked = PEEKED_NONE; // Search index of closing CDATA tag ]]> long index = indexOfClosingCDATA(); - String result = buffer.readUtf8(index); + String result = buffer.readString(index, charset); buffer.skip(3); // consume ]]> return result; } else if (p == PEEKED_ELEMENT_END) { @@ -673,7 +686,7 @@ public boolean nextTextContentAsBoolean() throws IOException { * @throws IOException */ private long indexOfClosingCDATA() throws IOException { - long index = source.indexOf(CDATA_CLOSE); + long index = source.indexOf(cdataClose); if (index == -1) { throw new EOFException(""); } @@ -810,12 +823,12 @@ private int nextNonWhitespace(boolean throwOnEof, boolean isDocumentBeginning) t int peekStack = stack[stackSize - 1]; if (peekStack == XmlScope.NONEMPTY_DOCUMENT && isDocTypeDefinition()) { - long index = source.indexOf(CLOSING_XML_ELEMENT, DOCTYPE_OPEN.size()); + long index = source.indexOf(CLOSING_XML_ELEMENT, doctypeOpen.size()); if (index == -1) { throw syntaxError("Unterminated . Inline DOCTYPE is not support at the moment."); } // check if doctype uses brackets - long bracketIndex = source.indexOf(OPENING_DOCTYPE_BRACKET, DOCTYPE_OPEN.size(), index); + long bracketIndex = source.indexOf(OPENING_DOCTYPE_BRACKET, doctypeOpen.size(), index); if (bracketIndex != -1) { index = source.indexOf(ByteString.of(CLOSING_DOCTYPE_BRACKET, CLOSING_XML_ELEMENT), index + bracketIndex); if (index == -1) { @@ -829,19 +842,19 @@ private int nextNonWhitespace(boolean throwOnEof, boolean isDocumentBeginning) t p = 0; continue; } else if (peek == '!' && fillBuffer(4)) { - long index = source.indexOf(COMMENT_CLOSE, 4); // skip + source.skip(index + commentClose.size()); // skip behind --!> p = 0; continue; } else if (peek == '?') { - long index = source.indexOf(XML_DECLARATION_CLOSE, 2); // skip + source.skip(index + xmlDeclarationClose.size()); // skip behind ?> p = 0; continue; } @@ -896,8 +909,8 @@ public String getCurrentElementName() { /** Returns an unquoted value as a string. */ private String nextUnquotedValue() throws IOException { - long i = source.indexOfElement(UNQUOTED_STRING_TERMINALS); - return i != -1 ? buffer.readUtf8(i) : buffer.readUtf8(); + long i = source.indexOfElement(unquotedStringTerminals); + return i != -1 ? buffer.readString(i, charset) : buffer.readString(charset); } /** @@ -920,7 +933,7 @@ private String nextQuotedValue(byte runTerminator) throws IOException { // If we've got an escape character, we're going to need a string builder. if (buffer.getByte(index) == '\\') { if (builder == null) builder = new StringBuilder(); - builder.append(buffer.readUtf8(index)); + builder.append(buffer.readString(index, charset)); buffer.readByte(); // '\' builder.append(readEscapeCharacter()); continue; @@ -928,11 +941,11 @@ private String nextQuotedValue(byte runTerminator) throws IOException { // If it isn't the escape character, it's the quote. Return the string. if (builder == null) { - String result = buffer.readUtf8(index); + String result = buffer.readString(index, charset); buffer.readByte(); // Consume the quote character. return result; } else { - builder.append(buffer.readUtf8(index)); + builder.append(buffer.readString(index, charset)); buffer.readByte(); // Consume the quote character. return builder.toString(); } @@ -988,7 +1001,7 @@ private char readEscapeCharacter() throws IOException { } else if (c >= 'A' && c <= 'F') { result += (c - 'A' + 10); } else { - throw syntaxError("\\u" + buffer.readUtf8(4)); + throw syntaxError("\\u" + buffer.readString(4, charset)); } } buffer.skip(4); diff --git a/core/src/main/java/com/tickaroo/tikxml/XmlWriter.java b/core/src/main/java/com/tickaroo/tikxml/XmlWriter.java index c4401577..2e5fcbde 100644 --- a/core/src/main/java/com/tickaroo/tikxml/XmlWriter.java +++ b/core/src/main/java/com/tickaroo/tikxml/XmlWriter.java @@ -18,15 +18,15 @@ package com.tickaroo.tikxml; -import java.io.Closeable; -import java.io.IOException; import okio.BufferedSink; import okio.ByteString; -import static com.tickaroo.tikxml.XmlScope.ELEMENT_CONTENT; -import static com.tickaroo.tikxml.XmlScope.ELEMENT_OPENING; -import static com.tickaroo.tikxml.XmlScope.NONEMPTY_DOCUMENT; -import static com.tickaroo.tikxml.XmlScope.getTopStackElementAsToken; +import java.io.Closeable; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import static com.tickaroo.tikxml.XmlScope.*; /** * With this class you can write xml with a convinient API. @@ -75,16 +75,16 @@ public class XmlWriter implements Closeable { private static final Byte DOUBLE_QUOTE = (byte) '"'; private static final Byte OPENING_XML_ELEMENT = (byte) '<'; private static final Byte CLOSING_XML_ELEMENT = (byte) '>'; - private static final ByteString CLOSING_XML_ELEMENT_START = ByteString.encodeUtf8(""); - private static final ByteString ATTRIBUTE_ASSIGNMENT_BEGIN = ByteString.encodeUtf8("=\""); - private static final ByteString OPENING_CDATA = ByteString.encodeUtf8(""); - private static final ByteString XML_DECLARATION = - ByteString.encodeUtf8(""); + private final ByteString closingXmlElementStart; + private final ByteString inlineClosingXmlElement; + private final ByteString attributeAssignmentBegin; + private final ByteString openingCdata; + private final ByteString closingCdata; + private final ByteString xmlDeclaration; /** The output data, containing at most one top-level array or object. */ private final BufferedSink sink; + private final Charset charset; private boolean xmlDeclarationWritten = false; private int[] stack = new int[32]; @@ -97,18 +97,30 @@ public class XmlWriter implements Closeable { stack[stackSize++] = XmlScope.EMPTY_DOCUMENT; } - private XmlWriter(BufferedSink sink) { + private XmlWriter(BufferedSink sink, Charset charset) { if (sink == null) { throw new NullPointerException("sink == null"); } this.sink = sink; + this.charset = charset; + + closingXmlElementStart = ByteString.encodeString("", charset); + attributeAssignmentBegin = ByteString.encodeString("=\"", charset); + openingCdata = ByteString.encodeString("", charset); + xmlDeclaration = ByteString.encodeString("", charset); } /** * Returns a new instance. */ public static XmlWriter of(BufferedSink source) { - return new XmlWriter(source); + return new XmlWriter(source, StandardCharsets.UTF_8); + } + + public static XmlWriter of(BufferedSink source, Charset charset) { + return new XmlWriter(source, charset); } private void pushStack(int newTop) { @@ -190,14 +202,14 @@ public XmlWriter beginElement(String elementTagName) throws IOException { pushStack(XmlScope.ELEMENT_OPENING); pathNames[stackSize - 1] = elementTagName; sink.writeByte(OPENING_XML_ELEMENT) - .writeUtf8(elementTagName); + .writeString(elementTagName, charset); break; case XmlScope.ELEMENT_CONTENT: // write a nested xml element Some optional text pushStack(XmlScope.ELEMENT_OPENING); pathNames[stackSize - 1] = elementTagName; sink.writeByte(OPENING_XML_ELEMENT) - .writeUtf8(elementTagName); + .writeString(elementTagName, charset); break; case XmlScope.ELEMENT_OPENING: // write a nested xml element by closing the parent's xml opening header @@ -206,7 +218,7 @@ public XmlWriter beginElement(String elementTagName) throws IOException { pathNames[stackSize - 1] = elementTagName; sink.writeByte(CLOSING_XML_ELEMENT) .writeByte(OPENING_XML_ELEMENT) - .writeUtf8(elementTagName); + .writeString(elementTagName, charset); break; case XmlScope.NONEMPTY_DOCUMENT: @@ -234,12 +246,12 @@ public XmlWriter endElement() throws IOException { int topOfStack = peekStack(); switch (topOfStack) { case XmlScope.ELEMENT_OPENING: - sink.write(INLINE_CLOSING_XML_ELEMENT); + sink.write(inlineClosingXmlElement); popStack(); break; case XmlScope.ELEMENT_CONTENT: - sink.write(CLOSING_XML_ELEMENT_START) - .writeUtf8(pathNames[stackSize - 1]) + sink.write(closingXmlElementStart) + .writeString(pathNames[stackSize - 1], charset) .writeByte(CLOSING_XML_ELEMENT); popStack(); break; @@ -273,11 +285,11 @@ public XmlWriter textContent(String textContentValue) throws IOException { case ELEMENT_OPENING: sink.writeByte(CLOSING_XML_ELEMENT); replaceTopOfStack(XmlScope.ELEMENT_CONTENT); - sink.writeUtf8(textContentValue); + sink.writeString(textContentValue, charset); break; case ELEMENT_CONTENT: - sink.writeUtf8(textContentValue); + sink.writeString(textContentValue, charset); break; default: @@ -345,15 +357,15 @@ public XmlWriter textContentAsCData(String textContentValue) throws IOException case ELEMENT_OPENING: replaceTopOfStack(XmlScope.ELEMENT_CONTENT); sink.writeByte(CLOSING_XML_ELEMENT) - .write(OPENING_CDATA) - .writeUtf8(textContentValue) - .write(CLOSING_CDATA); + .write(openingCdata) + .writeString(textContentValue, charset) + .write(closingCdata); break; case ELEMENT_CONTENT: - sink.write(OPENING_CDATA) - .writeUtf8(textContentValue) - .write(CLOSING_CDATA); + sink.write(openingCdata) + .writeString(textContentValue, charset) + .write(closingCdata); break; default: @@ -386,9 +398,9 @@ public XmlWriter textContentAsCData(String textContentValue) throws IOException public XmlWriter attribute(String attributeName, String value) throws IOException { if (XmlScope.ELEMENT_OPENING == peekStack()) { sink.writeByte(' ') // Write a whitespace - .writeUtf8(attributeName) - .write(ATTRIBUTE_ASSIGNMENT_BEGIN) - .writeUtf8(value) + .writeString(attributeName, charset) + .write(attributeAssignmentBegin) + .writeString(value, charset) .writeByte(DOUBLE_QUOTE); } else { throw syntaxError("Error while trying to write attribute " @@ -459,17 +471,17 @@ public XmlWriter xmlDeclaration() throws IOException { if (!xmlDeclarationWritten) { if (peekStack() == XmlScope.EMPTY_DOCUMENT) { - sink.write(XML_DECLARATION); + sink.write(xmlDeclaration); xmlDeclarationWritten = true; } else { - throw syntaxError("Xml Declatraion " - + XML_DECLARATION.utf8() + throw syntaxError("Xml Declaration " + + xmlDeclaration.string(charset) + " can only be written at the beginning of a xml document! You are not at the beginning of a xml document: current xml scope is " + XmlScope.getTopStackElementAsToken(stackSize, stack)); } } else { throw new IOException("Xml declaration " - + XML_DECLARATION.utf8() + + xmlDeclaration.string(charset) + " has already been written in this xml document. Xml declaration can only be written once at the beginning of the document."); } diff --git a/core/src/test/java/com/tickaroo/tikxml/TikXmlBuilderTest.java b/core/src/test/java/com/tickaroo/tikxml/TikXmlBuilderTest.java index 621a6308..cfa0b80d 100644 --- a/core/src/test/java/com/tickaroo/tikxml/TikXmlBuilderTest.java +++ b/core/src/test/java/com/tickaroo/tikxml/TikXmlBuilderTest.java @@ -19,6 +19,8 @@ package com.tickaroo.tikxml; import java.io.IOException; +import java.nio.charset.StandardCharsets; + import org.junit.Assert; import org.junit.Test; @@ -51,4 +53,20 @@ public void addTypeAdapter() throws IOException { Assert.assertSame(converter, tikXml.config.getTypeConverter(Object.class)); } + + @Test + public void defaultCharsetUTF8() { + TikXml tikXml = new TikXml.Builder().build(); + + Assert.assertEquals(StandardCharsets.UTF_8, tikXml.config.charset()); + } + + @Test + public void charset() { + TikXml tikXml = new TikXml.Builder() + .charset(StandardCharsets.ISO_8859_1) + .build(); + + Assert.assertEquals(StandardCharsets.ISO_8859_1, tikXml.config.charset()); + } } diff --git a/core/src/test/java/com/tickaroo/tikxml/XmlReaderTest.java b/core/src/test/java/com/tickaroo/tikxml/XmlReaderTest.java index f57c141c..aac38fd2 100644 --- a/core/src/test/java/com/tickaroo/tikxml/XmlReaderTest.java +++ b/core/src/test/java/com/tickaroo/tikxml/XmlReaderTest.java @@ -19,6 +19,7 @@ package com.tickaroo.tikxml; import java.io.IOException; +import java.nio.charset.StandardCharsets; import okio.Buffer; import org.junit.Assert; @@ -1157,4 +1158,14 @@ public void readXmlWithOpenAngleBracketAtEndOfBuffer() throws IOException { } } } + + @Test + public void iso_8859_1() throws IOException { + try (XmlReader reader = XmlReader.of(TestUtils.sourceForFile("iso_8859_1.xml"), StandardCharsets.ISO_8859_1)) { + reader.beginElement(); + reader.nextElementName(); + Assert.assertEquals("Lorem superposés valise pourparlers rêver chiots rendez-vous naissance Eiffel myrtille.", reader.nextTextContent()); + reader.endElement(); + } + } } diff --git a/core/src/test/java/com/tickaroo/tikxml/XmlWriterTest.java b/core/src/test/java/com/tickaroo/tikxml/XmlWriterTest.java index 1b54fbf9..6c0d6cb7 100644 --- a/core/src/test/java/com/tickaroo/tikxml/XmlWriterTest.java +++ b/core/src/test/java/com/tickaroo/tikxml/XmlWriterTest.java @@ -1,7 +1,11 @@ package com.tickaroo.tikxml; import java.io.IOException; +import java.nio.charset.StandardCharsets; + import okio.Buffer; +import okio.BufferedSource; +import okio.ByteString; import org.junit.*; /** @@ -435,7 +439,7 @@ public void xmlDeclarationInBeginElementScope() throws IOException { Assert.fail("Exception expected"); } catch (IOException e) { Assert.assertEquals( - "Xml Declatraion can only be written at the beginning of a xml document! You are not at the beginning of a xml document: current xml scope is ELEMENT_OPENING at path /foo", + "Xml Declaration can only be written at the beginning of a xml document! You are not at the beginning of a xml document: current xml scope is ELEMENT_OPENING at path /foo", e.getMessage()); } } @@ -452,7 +456,7 @@ public void xmlDeclarationInTextContentScope() throws IOException { Assert.fail("Exception expected"); } catch (IOException e) { Assert.assertEquals( - "Xml Declatraion can only be written at the beginning of a xml document! You are not at the beginning of a xml document: current xml scope is ELEMENT_CONTENT at path /foo/text()", + "Xml Declaration can only be written at the beginning of a xml document! You are not at the beginning of a xml document: current xml scope is ELEMENT_CONTENT at path /foo/text()", e.getMessage()); } } @@ -470,7 +474,7 @@ public void xmlDeclarationInNonEmptyDocumentScope() throws IOException { Assert.fail("Exception expected"); } catch (IOException e) { Assert.assertEquals( - "Xml Declatraion can only be written at the beginning of a xml document! You are not at the beginning of a xml document: current xml scope is NONEMPTY_DOCUMENT at path /", + "Xml Declaration can only be written at the beginning of a xml document! You are not at the beginning of a xml document: current xml scope is NONEMPTY_DOCUMENT at path /", e.getMessage()); } } @@ -543,4 +547,74 @@ public void throwExceptionBecauseNamespaceInNonEmptyDocimentScope() throws IOExc Assert.assertEquals("Error while trying to write attribute xmlns:m=\"http://foo.com\". Attributes can only be written in a opening xml element but was in xml scope NONEMPTY_DOCUMENT at path /", e.getMessage()); } } + + @Test + public void validElementContentContinuation() throws IOException { + Buffer buffer = new Buffer(); + XmlWriter writer = XmlWriter.of(buffer) + .xmlDeclaration() + .beginElement("root") + .beginElement("foo") + .attribute("other", 123) + .textContent("hello") + .textContent(" ") + .textContent("world") + .endElement() + .endElement(); + + Assert.assertEquals( + "hello world", + TestUtils.bufferToString(buffer)); + } + + @Test + public void validElementContentCData() throws IOException { + Buffer buffer = new Buffer(); + XmlWriter writer = XmlWriter.of(buffer) + .xmlDeclaration() + .beginElement("root") + .beginElement("foo") + .attribute("other", 123) + .textContent("hello world") + .textContentAsCData("Hello<>world") + .textContent("hello world") + .endElement() + .endElement(); + + Assert.assertEquals( + "hello worldworld]]>hello world", + TestUtils.bufferToString(buffer)); + } + + @Test + public void xmlDeclarationCharset() throws IOException { + Buffer buffer = new Buffer(); + XmlWriter.of(buffer, StandardCharsets.ISO_8859_1) + .xmlDeclaration() + .beginElement("root") + .endElement() + .close(); + String actual = TestUtils.bufferToString(buffer); + String expected = ""; + + Assert.assertEquals(expected, actual); + } + + @Test + public void iso_8859_1() throws IOException { + Buffer buffer = new Buffer(); + ByteString encoded = ByteString.encodeUtf8("Lorem superposés valise pourparlers rêver chiots rendez-vous naissance Eiffel myrtille."); + XmlWriter.of(buffer, StandardCharsets.ISO_8859_1) + .xmlDeclaration() + .beginElement("root") + .textContent(encoded.string(StandardCharsets.ISO_8859_1)) + .endElement() + .close(); + String actual = TestUtils.bufferToString(buffer); + + String expected = TestUtils.sourceForFile("iso_8859_1.xml") + .readString(StandardCharsets.ISO_8859_1); + + Assert.assertEquals(expected, actual); + } } diff --git a/core/src/test/resources/iso_8859_1.xml b/core/src/test/resources/iso_8859_1.xml new file mode 100644 index 00000000..a2108a10 --- /dev/null +++ b/core/src/test/resources/iso_8859_1.xml @@ -0,0 +1 @@ +Lorem superposés valise pourparlers rêver chiots rendez-vous naissance Eiffel myrtille. \ No newline at end of file