From 7795579dfeabfc31b2d06f1e17f1ef341bf15750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20D=C3=B6ring?= Date: Mon, 22 Feb 2021 21:46:50 +0100 Subject: [PATCH] #2013: add support for reading from / writing to a Path And adding base tests for all createGenerator(..) and createParser(..) methods. --- .../jackson/core/TokenStreamFactory.java | 70 ++++++++++ .../jackson/core/base/TextualTSFactory.java | 25 ++++ .../jackson/core/json/JsonFactoryTest.java | 120 ++++++++++++++++++ 3 files changed, 215 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java b/src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java index c388a7b174..f03a16d3ee 100644 --- a/src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java @@ -7,6 +7,8 @@ import java.io.*; import java.lang.ref.SoftReference; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Locale; @@ -703,6 +705,32 @@ public PropertyNameMatcher constructCINameMatcher(List matches, boolean a public abstract JsonParser createParser(ObjectReadContext readCtxt, File f) throws JacksonException; + /** + * Method for constructing parser instance to decode + * contents of specified path. + *

+ * Encoding is auto-detected from contents according to JSON + * specification recommended mechanism. Json specification + * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, + * so auto-detection implemented only for this charsets. + * For other charsets use {@link #createParser(java.io.Reader)}. + *

+ * Underlying input stream (needed for reading contents) + * will be owned (and managed, i.e. closed as need be) by + * the parser, since caller has no access to it. + * + * @param readCtxt Object read context to use + * @param p Path that contains content to parse + * + * @return Parser constructed + * + * @throws JacksonException If parser construction or initialization fails + * + * @since 3.0 + */ + public abstract JsonParser createParser(ObjectReadContext readCtxt, + Path p) throws JacksonException; + /** * Method for constructing JSON parser instance to decode * contents of resource reference by given URL. @@ -1131,6 +1159,30 @@ public abstract JsonGenerator createGenerator(ObjectWriteContext writeCtxt, File JsonEncoding enc) throws JacksonException; + /** + * Method for constructing generator that writes contents + * to specified path, overwriting contents it might have (or creating + * it if such path does not yet exist). + *

+ * Underlying stream is owned by the generator constructed, + * i.e. generator will handle closing of path when + * {@link JsonGenerator#close} is called. + * + * @param writeCtxt Object-binding context where applicable; used for providing contextual + * configuration + * @param p Path to write contents to + * @param enc Character set encoding to use (usually {@link JsonEncoding#UTF8}) + * + * @return Generator constructed + * + * @throws JacksonException If generator construction or initialization fails + * + * @since 3.0 + */ + public abstract JsonGenerator createGenerator(ObjectWriteContext writeCtxt, Path p, + JsonEncoding enc) + throws JacksonException; + /** * Method for constructing generator that writes content into specified {@link DataOutput}, * using UTF-8 encoding (with formats where encoding is user-configurable). @@ -1329,6 +1381,15 @@ protected InputStream _fileInputStream(File f) throws JacksonException } } + protected InputStream _pathInputStream(Path p) throws JacksonException + { + try { + return Files.newInputStream(p); + } catch (IOException e) { + throw _wrapIOFailure(e); + } + } + protected OutputStream _fileOutputStream(File f) throws JacksonException { try { @@ -1338,6 +1399,15 @@ protected OutputStream _fileOutputStream(File f) throws JacksonException } } + protected OutputStream _pathOutputStream(Path p) throws JacksonException + { + try { + return Files.newOutputStream(p); + } catch (IOException e) { + throw _wrapIOFailure(e); + } + } + protected JacksonException _wrapIOFailure(IOException e) { return WrappedIOException.construct(e, this); } diff --git a/src/main/java/com/fasterxml/jackson/core/base/TextualTSFactory.java b/src/main/java/com/fasterxml/jackson/core/base/TextualTSFactory.java index 5321fd1a52..05c8254935 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/TextualTSFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/base/TextualTSFactory.java @@ -2,6 +2,7 @@ import java.io.*; import java.net.URL; +import java.nio.file.Path; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.IOContext; @@ -95,6 +96,16 @@ public JsonParser createParser(ObjectReadContext readCtxt, _decorate(ioCtxt, _fileInputStream(f))); } + @Override + public JsonParser createParser(ObjectReadContext readCtxt, + Path p) throws JacksonException + { + // true, since we create InputStream from Path + IOContext ioCtxt = _createContext(p, true); + return _createParser(readCtxt, ioCtxt, + _decorate(ioCtxt, _pathInputStream(p))); + } + @Override public JsonParser createParser(ObjectReadContext readCtxt, URL url) throws JacksonException @@ -233,6 +244,20 @@ public JsonGenerator createGenerator(ObjectWriteContext writeCtxt, _decorate(ioCtxt, _createWriter(ioCtxt, out, enc))); } + @Override + public JsonGenerator createGenerator(ObjectWriteContext writeCtxt, + Path p, JsonEncoding enc) + throws JacksonException + { + final OutputStream out = _pathOutputStream(p); + final IOContext ioCtxt = _createContext(p, true, enc); + if (enc == JsonEncoding.UTF8) { + return _createUTF8Generator(writeCtxt, ioCtxt, _decorate(ioCtxt, out)); + } + return _createGenerator(writeCtxt, ioCtxt, + _decorate(ioCtxt, _createWriter(ioCtxt, out, enc))); + } + /* /********************************************************************** /* Factory methods: abstract, for sub-classes to implement diff --git a/src/test/java/com/fasterxml/jackson/core/json/JsonFactoryTest.java b/src/test/java/com/fasterxml/jackson/core/json/JsonFactoryTest.java index 069fb08b8a..5ed0f49441 100644 --- a/src/test/java/com/fasterxml/jackson/core/json/JsonFactoryTest.java +++ b/src/test/java/com/fasterxml/jackson/core/json/JsonFactoryTest.java @@ -1,6 +1,8 @@ package com.fasterxml.jackson.core.json; import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.SerializedString; @@ -200,4 +202,122 @@ public void testRootValues() throws Exception g.close(); assertEquals("1/2/3", w.toString()); } + + public void test_createGenerator_OutputStream() throws Exception + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + JsonGenerator jsonGenerator = new JsonFactory() + .createGenerator(ObjectWriteContext.empty(), outputStream); + + jsonGenerator.writeString("value"); + jsonGenerator.close(); + + assertEquals(new String(outputStream.toByteArray(), StandardCharsets.UTF_8), "\"value\""); + + // the stream has not been closed by close + outputStream.write(1); + } + + public void test_createGenerator_File() throws Exception + { + Path path = Files.createTempFile("", ""); + JsonGenerator jsonGenerator = new JsonFactory() + .createGenerator(ObjectWriteContext.empty(), path.toFile(), JsonEncoding.UTF8); + + jsonGenerator.writeString("value"); + jsonGenerator.close(); + + assertEquals(new String(Files.readAllBytes(path), StandardCharsets.UTF_8), "\"value\""); + } + + public void test_createGenerator_Path() throws Exception + { + Path path = Files.createTempFile("", ""); + JsonGenerator jsonGenerator = new JsonFactory() + .createGenerator(ObjectWriteContext.empty(), path, JsonEncoding.UTF8); + + jsonGenerator.writeString("value"); + jsonGenerator.close(); + + assertEquals(new String(Files.readAllBytes(path), StandardCharsets.UTF_8), "\"value\""); + } + + public void test_createGenerator_Writer() throws Exception + { + Writer writer = new StringWriter(); + JsonGenerator jsonGenerator = new JsonFactory() + .createGenerator(ObjectWriteContext.empty(), writer); + + jsonGenerator.writeString("value"); + jsonGenerator.close(); + + assertEquals(writer.toString(), "\"value\""); + + // the writer has not been closed by close + writer.append('1'); + } + + public void test_createGenerator_DataOutput() throws Exception + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + DataOutput dataOutput = new DataOutputStream(outputStream); + JsonGenerator jsonGenerator = new JsonFactory() + .createGenerator(ObjectWriteContext.empty(), dataOutput); + + jsonGenerator.writeString("value"); + jsonGenerator.close(); + + assertEquals(new String(outputStream.toByteArray(), StandardCharsets.UTF_8), "\"value\""); + + // the data output has not been closed by close + dataOutput.write(1); + } + + public void test_createParser_OutputStream() throws Exception + { + ByteArrayInputStream inputStream = new ByteArrayInputStream("\"value\"".getBytes(StandardCharsets.UTF_8)); + JsonParser jsonParser = new JsonFactory() + .createParser(ObjectReadContext.empty(), inputStream); + + assertEquals(jsonParser.nextTextValue(), "value"); + } + + public void test_createParser_File() throws Exception + { + Path path = Files.createTempFile("", ""); + Files.write(path, "\"value\"".getBytes(StandardCharsets.UTF_8)); + JsonParser jsonParser = new JsonFactory() + .createParser(ObjectReadContext.empty(), path.toFile()); + + assertEquals(jsonParser.nextTextValue(), "value"); + } + + public void test_createParser_Path() throws Exception + { + Path path = Files.createTempFile("", ""); + Files.write(path, "\"value\"".getBytes(StandardCharsets.UTF_8)); + JsonParser jsonParser = new JsonFactory() + .createParser(ObjectReadContext.empty(), path); + + assertEquals(jsonParser.nextTextValue(), "value"); + } + + public void test_createParser_Reader() throws Exception + { + Reader reader = new StringReader("\"value\""); + JsonParser jsonParser = new JsonFactory() + .createParser(ObjectReadContext.empty(), reader); + + assertEquals(jsonParser.nextTextValue(), "value"); + } + + public void test_createParser_DataInput() throws Exception + { + InputStream inputStream = new ByteArrayInputStream("\"value\"".getBytes(StandardCharsets.UTF_8)); + DataInput dataInput = new DataInputStream(inputStream); + JsonParser jsonParser = new JsonFactory() + .createParser(ObjectReadContext.empty(), dataInput); + + assertEquals(jsonParser.nextTextValue(), "value"); + } }