Skip to content

Commit

Permalink
#2013: add support for reading from / writing to a Path
Browse files Browse the repository at this point in the history
And adding base tests for all createGenerator(..) and createParser(..) methods.
  • Loading branch information
sdoeringNew committed Feb 26, 2021
1 parent 44a5c9f commit 7795579
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 0 deletions.
70 changes: 70 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -703,6 +705,32 @@ public PropertyNameMatcher constructCINameMatcher(List<Named> matches, boolean a
public abstract JsonParser createParser(ObjectReadContext readCtxt,
File f) throws JacksonException;

/**
* Method for constructing parser instance to decode
* contents of specified path.
*<p>
* 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)}.
*<p>
* Underlying input stream (needed for reading contents)
* will be <b>owned</b> (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.
Expand Down Expand Up @@ -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).
*<p>
* Underlying stream <b>is owned</b> 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).
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
120 changes: 120 additions & 0 deletions src/test/java/com/fasterxml/jackson/core/json/JsonFactoryTest.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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");
}
}

0 comments on commit 7795579

Please sign in to comment.