From 27dd2d9b0dda843b6a9419ec72cc77173dbfaf33 Mon Sep 17 00:00:00 2001 From: David Golub Date: Fri, 11 Dec 2015 17:39:54 -0500 Subject: [PATCH] Add JsonGenerator feature to use single quotation marks --- .../fasterxml/jackson/core/JsonGenerator.java | 9 ++ .../jackson/core/base/GeneratorBase.java | 4 + .../fasterxml/jackson/core/io/CharTypes.java | 13 +++ .../jackson/core/json/JsonGeneratorImpl.java | 17 +++- .../jackson/core/json/UTF8JsonGenerator.java | 92 +++++++++++-------- .../core/json/WriterBasedJsonGenerator.java | 69 ++++++++------ .../core/json/TestJsonGeneratorFeatures.java | 33 +++++++ 7 files changed, 168 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java index 2cba09ef4d..332fdb9814 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java @@ -210,6 +210,15 @@ public enum Feature { * @since 2.5 */ IGNORE_UNKNOWN(false), + + /** + * Features that determines whether generator will write strings with single quotes + * (apostrophe, character '\'') instead of double quotes. + *

+ * Since JSON specification requires use of double quotes for field names, this is a + * non-standard feature, and as such is disabled by default. + */ + WRITE_SINGLE_QUOTES(false), ; private final boolean _defaultState; diff --git a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java index 38302b5ce4..c4847b9605 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java @@ -150,6 +150,8 @@ public JsonGenerator enable(Feature f) { if (_writeContext.getDupDetector() == null) { // but only if disabled currently _writeContext = _writeContext.withDupDetector(DupDetector.rootDetector(this)); } + } else if (f == Feature.WRITE_SINGLE_QUOTES) { + setCharacterEscapes(getCharacterEscapes()); } } return this; @@ -166,6 +168,8 @@ public JsonGenerator disable(Feature f) { setHighestNonEscapedChar(0); } else if (f == Feature.STRICT_DUPLICATE_DETECTION) { _writeContext = _writeContext.withDupDetector(null); + } else if (f == Feature.WRITE_SINGLE_QUOTES) { + setCharacterEscapes(getCharacterEscapes()); } } return this; diff --git a/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java b/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java index 0216fe045f..d512765009 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java +++ b/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java @@ -154,6 +154,7 @@ public final class CharTypes * 7-bit ASCII range need to be quoted. */ final static int[] sOutputEscapes128; + final static int[] sOutputEscapes128SingleQuoted; static { int[] table = new int[128]; // Control chars need generic escape sequence @@ -173,6 +174,9 @@ public final class CharTypes table[0x0A] = 'n'; table[0x0D] = 'r'; sOutputEscapes128 = table; + sOutputEscapes128SingleQuoted = Arrays.copyOf(table, 128); + sOutputEscapes128SingleQuoted['"'] = 0; + sOutputEscapes128SingleQuoted['\''] = '\''; } /** @@ -210,6 +214,15 @@ public final class CharTypes */ public static int[] get7BitOutputEscapes() { return sOutputEscapes128; } + /** + * Accessor for getting a read-only encoding table for first 128 Unicode + * code points (single-byte UTF-8 characters) in single quoted strings. + * Value of 0 means "no escaping"; other positive values that value is character + * to use after backslash; and negative values that generic (backslash - u) + * escpaing is to be used. + */ + public static int[] get7BitOutputEscapesSingleQuoted() { return sOutputEscapes128SingleQuoted; } + public static int charToHex(int ch) { return (ch > 127) ? -1 : sHexValues[ch]; diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java b/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java index 9ce93e4321..a381bbfa15 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java +++ b/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java @@ -29,7 +29,9 @@ public abstract class JsonGeneratorImpl extends GeneratorBase * (first 128 character codes), used for single-byte UTF-8 characters. */ protected final static int[] sOutputEscapes = CharTypes.get7BitOutputEscapes(); - + protected final static int[] sOutputEscapesSingleQuoted + = CharTypes.get7BitOutputEscapesSingleQuoted(); + /* /********************************************************** /* Configuration, basic I/O @@ -50,7 +52,7 @@ public abstract class JsonGeneratorImpl extends GeneratorBase * character codes). Defined separately to make potentially * customizable */ - protected int[] _outputEscapes = sOutputEscapes; + protected int[] _outputEscapes; /** * Value between 128 (0x80) and 65535 (0xFFFF) that indicates highest @@ -97,6 +99,11 @@ public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec) if (isEnabled(Feature.ESCAPE_NON_ASCII)) { setHighestNonEscapedChar(127); } + if (isEnabled(Feature.WRITE_SINGLE_QUOTES)) { + _outputEscapes = sOutputEscapesSingleQuoted; + } else { + _outputEscapes = sOutputEscapes; + } } /* @@ -121,7 +128,11 @@ public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { _characterEscapes = esc; if (esc == null) { // revert to standard escapes - _outputEscapes = sOutputEscapes; + if (isEnabled(Feature.WRITE_SINGLE_QUOTES)) { + _outputEscapes = sOutputEscapesSingleQuoted; + } else { + _outputEscapes = sOutputEscapes; + } } else { _outputEscapes = esc.getEscapeCodesForAscii(); } diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java index 70f17d8daa..b409419d99 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java @@ -23,6 +23,7 @@ public class UTF8JsonGenerator private final static byte BYTE_COMMA = (byte) ','; private final static byte BYTE_COLON = (byte) ':'; private final static byte BYTE_QUOTE = (byte) '"'; + private final static byte BYTE_SINGLE_QUOTE = (byte) '\''; // intermediate copies only made up to certain length... private final static int MAX_BYTES_TO_BUFFER = 512; @@ -103,6 +104,11 @@ public class UTF8JsonGenerator */ protected boolean _cfgUnqNames; + /** + * Flag that is set if single quotation marks are to be used. + */ + protected boolean _cfgSingleQuotes; + /* /********************************************************** /* Life-cycle @@ -131,6 +137,7 @@ public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, setHighestNonEscapedChar(127); } _cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(features); + _cfgSingleQuotes = Feature.WRITE_SINGLE_QUOTES.enabledIn(features); } public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, @@ -149,6 +156,7 @@ public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, _charBuffer = ctxt.allocConcatBuffer(); _charBufferLength = _charBuffer.length; _cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(features); + _cfgSingleQuotes = !Feature.WRITE_SINGLE_QUOTES.enabledIn(features); } /* @@ -207,7 +215,7 @@ public void writeFieldName(String name) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); // But as one segment, or multiple? if (len <= _outputMaxContiguous) { if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space @@ -221,7 +229,7 @@ public void writeFieldName(String name) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -248,7 +256,7 @@ public void writeFieldName(SerializableString name) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); if (len < 0) { // couldn't append, bit longer processing _writeBytes(name.asQuotedUTF8()); @@ -258,8 +266,8 @@ public void writeFieldName(SerializableString name) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; - } + _writeQuote(); + } private final void _writeUnq(SerializableString name) throws IOException { int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); @@ -367,7 +375,7 @@ protected final void _writePPFieldName(String name) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); name.getChars(0, len, _charBuffer, 0); // But as one segment, or multiple? if (len <= _outputMaxContiguous) { @@ -381,7 +389,7 @@ protected final void _writePPFieldName(String name) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } protected final void _writePPFieldName(SerializableString name) throws IOException @@ -401,14 +409,14 @@ protected final void _writePPFieldName(SerializableString name) throws IOExcepti if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } _writeBytes(name.asQuotedUTF8()); if (addQuotes) { if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } } @@ -435,12 +443,12 @@ public void writeString(String text) throws IOException if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); _writeStringSegment(text, 0, len); // we checked space already above if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -450,7 +458,7 @@ public void writeString(char[] text, int offset, int len) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); // One or multiple segments? if (len <= _outputMaxContiguous) { if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space @@ -464,7 +472,7 @@ public void writeString(char[] text, int offset, int len) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -474,7 +482,7 @@ public final void writeString(SerializableString text) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); int len = text.appendQuotedUTF8(_outputBuffer, _outputTail); if (len < 0) { _writeBytes(text.asQuotedUTF8()); @@ -484,7 +492,7 @@ public final void writeString(SerializableString text) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -494,12 +502,12 @@ public void writeRawUTF8String(byte[] text, int offset, int length) throws IOExc if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); _writeBytes(text, offset, length); if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -509,7 +517,7 @@ public void writeUTF8String(byte[] text, int offset, int len) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); // One or multiple segments? if (len <= _outputMaxContiguous) { _writeUTF8Segment(text, offset, len); @@ -519,7 +527,7 @@ public void writeUTF8String(byte[] text, int offset, int len) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } /* @@ -697,13 +705,13 @@ public void writeBinary(Base64Variant b64variant, if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); _writeBinary(b64variant, data, offset, offset+len); // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -716,7 +724,7 @@ public int writeBinary(Base64Variant b64variant, if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); byte[] encodingBuffer = _ioContext.allocBase64Buffer(); int bytes; try { @@ -736,7 +744,7 @@ public int writeBinary(Base64Variant b64variant, if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); return bytes; } @@ -765,10 +773,10 @@ private final void _writeQuotedShort(short s) throws IOException { if ((_outputTail + 8) >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); - _outputBuffer[_outputTail++] = BYTE_QUOTE; - } + _writeQuote(); + } @Override public void writeNumber(int i) throws IOException @@ -790,10 +798,10 @@ private final void _writeQuotedInt(int i) throws IOException if ((_outputTail + 13) >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); - _outputBuffer[_outputTail++] = BYTE_QUOTE; - } + _writeQuote(); + } @Override public void writeNumber(long l) throws IOException @@ -815,9 +823,9 @@ private final void _writeQuotedLong(long l) throws IOException if ((_outputTail + 23) >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -897,12 +905,12 @@ private final void _writeQuotedRaw(String value) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); writeRaw(value); if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } @Override @@ -1112,7 +1120,19 @@ private final void _writeBytes(byte[] bytes, int offset, int len) throws IOExcep /* Internal methods, mid-level writing, String segments /********************************************************** */ - + + /** + * Method called to write a single or double quotation mark, + * depending on whether the feature is enabled. + */ + private final void _writeQuote() { + if (_cfgSingleQuotes) { + _outputBuffer[_outputTail++] = BYTE_SINGLE_QUOTE; + } else { + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + } + /** * Method called when String to write is long enough not to fit * completely in temporary copy buffer. If so, we will actually @@ -1126,7 +1146,7 @@ private final void _writeStringSegments(String text, boolean addQuotes) throws I if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } int left = text.length(); @@ -1146,7 +1166,7 @@ private final void _writeStringSegments(String text, boolean addQuotes) throws I if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeQuote(); } } diff --git a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java index 4e9196922a..f723cc71bc 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java @@ -215,14 +215,14 @@ protected void _writeFieldName(String name, boolean commaBefore) throws IOExcept } // we know there's room for at least one more char - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); // The beef: _writeString(name); // and closing quotes; need room for one more char: if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } protected void _writeFieldName(SerializableString name, boolean commaBefore) throws IOException @@ -247,7 +247,7 @@ protected void _writeFieldName(SerializableString name, boolean commaBefore) thr return; } // we know there's room for at least one more char - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); // The beef: final int qlen = quoted.length; if ((_outputTail + qlen + 1) >= _outputEnd) { @@ -256,11 +256,11 @@ protected void _writeFieldName(SerializableString name, boolean commaBefore) thr if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } else { System.arraycopy(quoted, 0, _outputBuffer, _outputTail, qlen); _outputTail += qlen; - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } } @@ -281,12 +281,12 @@ protected void _writePPFieldName(String name, boolean commaBefore) if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); _writeString(name); if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } else { // non-standard, omit quotes _writeString(name); } @@ -306,12 +306,12 @@ protected void _writePPFieldName(SerializableString name, boolean commaBefore) if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); writeRaw(quoted, 0, quoted.length); if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } else { // non-standard, omit quotes writeRaw(quoted, 0, quoted.length); } @@ -334,13 +334,13 @@ public void writeString(String text) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); _writeString(text); // And finally, closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } @Override @@ -350,13 +350,13 @@ public void writeString(char[] text, int offset, int len) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); _writeString(text, offset, len); // And finally, closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } @Override @@ -366,7 +366,7 @@ public void writeString(SerializableString sstr) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); // Note: copied from writeRaw: char[] text = sstr.asQuotedChars(); final int len = text.length; @@ -386,7 +386,7 @@ public void writeString(SerializableString sstr) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } @Override @@ -441,7 +441,7 @@ public void writeRaw(String text, int start, int len) throws IOException if (room >= len) { text.getChars(start, start+len, _outputBuffer, _outputTail); _outputTail += len; - } else { + } else { writeRawLong(text.substring(start, start+len)); } } @@ -519,13 +519,13 @@ public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int l if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); _writeBinary(b64variant, data, offset, offset+len); // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } @Override @@ -538,7 +538,7 @@ public int writeBinary(Base64Variant b64variant, if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); byte[] encodingBuffer = _ioContext.allocBase64Buffer(); int bytes; try { @@ -558,7 +558,7 @@ public int writeBinary(Base64Variant b64variant, if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); return bytes; } @@ -587,10 +587,10 @@ private void _writeQuotedShort(short s) throws IOException { if ((_outputTail + 8) >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); - _outputBuffer[_outputTail++] = '"'; - } + _writeQuote(); + } @Override public void writeNumber(int i) throws IOException @@ -611,10 +611,10 @@ private void _writeQuotedInt(int i) throws IOException { if ((_outputTail + 13) >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); - _outputBuffer[_outputTail++] = '"'; - } + _writeQuote(); + } @Override public void writeNumber(long l) throws IOException @@ -635,9 +635,9 @@ private void _writeQuotedLong(long l) throws IOException { if ((_outputTail + 23) >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } // !!! 05-Aug-2008, tatus: Any ways to optimize these? @@ -717,12 +717,12 @@ private void _writeQuotedRaw(String value) throws IOException if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); writeRaw(value); if (_outputTail >= _outputEnd) { _flushBuffer(); } - _outputBuffer[_outputTail++] = '"'; + _writeQuote(); } @Override @@ -906,6 +906,15 @@ protected void _releaseBuffers() /********************************************************** */ + private void _writeQuote() + { + if (isEnabled(Feature.WRITE_SINGLE_QUOTES)) { + _outputBuffer[_outputTail++] = '\''; + } else { + _outputBuffer[_outputTail++] = '"'; + } + } + private void _writeString(String text) throws IOException { /* One check first: if String won't fit in the buffer, let's diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestJsonGeneratorFeatures.java b/src/test/java/com/fasterxml/jackson/core/json/TestJsonGeneratorFeatures.java index 5a42c83cc4..207542fcc9 100644 --- a/src/test/java/com/fasterxml/jackson/core/json/TestJsonGeneratorFeatures.java +++ b/src/test/java/com/fasterxml/jackson/core/json/TestJsonGeneratorFeatures.java @@ -49,6 +49,19 @@ public void testNonNumericQuoting() _testNonNumericQuoting(jf, true); } + public void testSingleQuoting() throws IOException + { + JsonFactory jf = new JsonFactory(); + // by default, single quoting should be disabled + _testSingleQuoting(jf, false); + // can enable it + jf.enable(JsonGenerator.Feature.WRITE_SINGLE_QUOTES); + _testSingleQuoting(jf, true); + // and disable it again + jf.disable(JsonGenerator.Feature.WRITE_SINGLE_QUOTES); + _testSingleQuoting(jf, false); + } + /** * Testing for [JACKSON-176], ability to force serializing numbers * as JSON Strings. @@ -172,4 +185,24 @@ private void _testNonNumericQuoting(JsonFactory jf, boolean quoted) assertEquals("{\"double\":NaN} {\"float\":NaN}", result); } } + private void _testSingleQuoting(JsonFactory jf, boolean singleQuoted) + throws IOException + { + StringWriter sw = new StringWriter(); + JsonGenerator jg = jf.createGenerator(sw); + jg.writeStartObject(); + jg.writeFieldName("single"); + jg.writeString("'"); + jg.writeFieldName("double"); + jg.writeString("\""); + jg.writeEndObject(); + jg.close(); + + String result = sw.toString(); + if (singleQuoted) { + assertEquals("{'single':'\\'','double':'\"'}", result); + } else { + assertEquals("{\"single\":\"'\",\"double\":\"\\\"\"}", result); + } + } }