diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java index 04e8bf99ee..b3c90dff41 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java @@ -957,6 +957,30 @@ public abstract int writeBinary(Base64Variant bv, */ public abstract void writeNumber(String encodedValue) throws IOException; + + /** + * Write method that can be used for custom numeric types that can + * not be (easily?) converted to "standard" Java number types. + * Because numbers are not surrounded by double quotes, regular + * {@link #writeString} method can not be used; nor + * {@link #writeRaw} because that does not properly handle + * value separators needed in Array or Object contexts. + *

+ * Note: because of lack of type safety, some generator + * implementations may not be able to implement this + * method. For example, if a binary JSON format is used, + * it may require type information for encoding; similarly + * for generator-wrappers around Java objects or JSON nodes. + * If implementation does not implement this method, + * it needs to throw {@link UnsupportedOperationException}. + * + * @throws UnsupportedOperationException If underlying data format does not + * support numbers serialized textually AND if generator is not allowed + * to just output a String instead (Schema-based formats may require actual + * number, for example) + */ + public abstract void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException; + /* /********************************************************************** /* Public API, write methods, other value types diff --git a/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java b/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java index 6b614365f9..aaa77a1fed 100644 --- a/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java +++ b/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java @@ -722,6 +722,27 @@ public void writeNumber(String encodedValue) throws IOException, UnsupportedOper delegate.writeNumber(encodedValue); } + @Override + public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException, UnsupportedOperationException + { + if (_itemFilter == null) { + return; + } + if (_itemFilter != TokenFilter.INCLUDE_ALL) { + TokenFilter state = _filterContext.checkValue(_itemFilter); + if (state == null) { + return; + } + if (state != TokenFilter.INCLUDE_ALL) { + if (!state.includeRawValue()) { // close enough? + return; + } + } + _checkParentPath(); + } + delegate.writeNumber(encodedValueBuffer, offset, length); + } + @Override public void writeBoolean(boolean v) throws IOException { 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 fb0671e2d3..59ff467414 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java @@ -1072,6 +1072,16 @@ public void writeNumber(String encodedValue) throws IOException } } + @Override + public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException { + _verifyValueWrite(WRITE_NUMBER); + if (_cfgNumbersAsStrings) { + _writeQuotedRaw(encodedValueBuffer, offset, length); + } else { + writeRaw(encodedValueBuffer, offset, length); + } + } + private final void _writeQuotedRaw(String value) throws IOException { if (_outputTail >= _outputEnd) { @@ -1084,7 +1094,20 @@ private final void _writeQuotedRaw(String value) throws IOException } _outputBuffer[_outputTail++] = _quoteChar; } - + + private void _writeQuotedRaw(char[] text, int offset, int length) throws IOException + { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = _quoteChar; + writeRaw(text, offset, length); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = _quoteChar; + } + @Override public void writeBoolean(boolean state) throws IOException { 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 6484a9aa59..651bd33ea3 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java @@ -866,6 +866,16 @@ public void writeNumber(String encodedValue) throws IOException } } + @Override + public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException { + _verifyValueWrite(WRITE_NUMBER); + if (_cfgNumbersAsStrings) { + _writeQuotedRaw(encodedValueBuffer, offset, length); + } else { + writeRaw(encodedValueBuffer, offset, length); + } + } + private void _writeQuotedRaw(String value) throws IOException { if (_outputTail >= _outputEnd) { @@ -879,6 +889,19 @@ private void _writeQuotedRaw(String value) throws IOException _outputBuffer[_outputTail++] = _quoteChar; } + private void _writeQuotedRaw(char[] text, int offset, int length) throws IOException + { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = _quoteChar; + writeRaw(text, offset, length); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = _quoteChar; + } + @Override public void writeBoolean(boolean state) throws IOException { diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java b/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java index f2fc626444..8f1d491279 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java @@ -294,6 +294,9 @@ public void writeString(Reader reader, int len) throws IOException { @Override public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException { delegate.writeNumber(encodedValue); } + @Override + public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException, UnsupportedOperationException { delegate.writeNumber(encodedValueBuffer, offset, length); } + @Override public void writeBoolean(boolean state) throws IOException { delegate.writeBoolean(state); } diff --git a/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java index ee0ded4338..2343f0747b 100644 --- a/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java +++ b/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java @@ -340,7 +340,7 @@ public void testWriteStartObjectWithObject() throws Exception } // [core#580] - public void testRawValueDelegation() throws Exception + public void testRawValueDelegationWithArray() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(ObjectWriteContext.empty(), w), @@ -354,4 +354,23 @@ public void testRawValueDelegation() throws Exception gen.close(); assertEquals("[1,2]", w.toString()); } + + // [core#588] + public void testRawValueDelegationWithObject() throws Exception + { + StringWriter w = new StringWriter(); + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(ObjectWriteContext.empty(), w), + TokenFilter.INCLUDE_ALL, true, true); + + gen.writeStartObject(); + gen.writeNumberField("f1", 1); + gen.writeFieldName("f2"); + gen.writeRawValue(new char[]{'1', '2', '.', '3', '-'}, 0, 4); + gen.writeNumberField("f3", 3); + gen.writeEndObject(); + + gen.close(); + assertEquals(aposToQuotes("{'f1':1,'f2':12.3,'f3':3}"), w.toString()); + } + } diff --git a/src/test/java/com/fasterxml/jackson/core/json/StreamWriteFeaturesTest.java b/src/test/java/com/fasterxml/jackson/core/json/StreamWriteFeaturesTest.java index ab4f90c57c..5e1b9af58f 100644 --- a/src/test/java/com/fasterxml/jackson/core/json/StreamWriteFeaturesTest.java +++ b/src/test/java/com/fasterxml/jackson/core/json/StreamWriteFeaturesTest.java @@ -71,15 +71,15 @@ public void testNumbersAsJSONStrings() throws IOException { JsonFactory f = new JsonFactory(); // by default should output numbers as-is: - assertEquals("[1,2,3,1.25,2.25,3001,0.5,-1]", _writeNumbers(f, false)); - assertEquals("[1,2,3,1.25,2.25,3001,0.5,-1]", _writeNumbers(f, true)); + assertEquals("[1,2,3,1.25,2.25,3001,0.5,-1,12.3]", _writeNumbers(f, false)); + assertEquals("[1,2,3,1.25,2.25,3001,0.5,-1,12.3]", _writeNumbers(f, true)); // but if overridden, quotes as Strings f = f.rebuild().configure(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS, true) .build(); - assertEquals("[\"1\",\"2\",\"3\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\"]", + assertEquals("[\"1\",\"2\",\"3\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\",\"12.3\"]", _writeNumbers(f, false)); - assertEquals("[\"1\",\"2\",\"3\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\"]", + assertEquals("[\"1\",\"2\",\"3\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\",\"12.3\"]", _writeNumbers(f, true)); } @@ -193,6 +193,7 @@ private String _writeNumbers(JsonFactory f, boolean useBytes) throws IOException g.writeNumber(BigInteger.valueOf(3001)); g.writeNumber(BigDecimal.valueOf(0.5)); g.writeNumber("-1"); + g.writeNumber(new char[]{'1', '2', '.', '3', '-'}, 0, 4); g.writeEndArray(); g.close();