JsonGenerator.java
/* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package tools.jackson.core;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Objects;
import tools.jackson.core.JsonParser.NumberType;
import tools.jackson.core.exc.JacksonIOException;
import tools.jackson.core.exc.StreamReadException;
import tools.jackson.core.exc.StreamWriteException;
import tools.jackson.core.io.CharacterEscapes;
import tools.jackson.core.type.WritableTypeId;
import tools.jackson.core.type.WritableTypeId.Inclusion;
import tools.jackson.core.util.JacksonFeatureSet;
import static tools.jackson.core.JsonTokenId.*;
/**
* Base class that defines public API for writing JSON content.
* Instances are created using factory methods of
* a {@link TokenStreamFactory} instance.
*
* @author Tatu Saloranta
*/
public abstract class JsonGenerator
implements Closeable, Flushable, Versioned
{
/*
/**********************************************************************
/* Construction, initialization
/**********************************************************************
*/
protected JsonGenerator() { }
/*
/**********************************************************************
/* Versioned
/**********************************************************************
*/
/**
* Accessor for finding out version of the bundle that provided this generator instance.
*/
@Override
public abstract Version version();
/*
/**********************************************************************
/* Constraints violation checking
/**********************************************************************
*/
/**
* Get the constraints to apply when performing streaming writes.
*
* @return Constraints used for this generator
*/
public StreamWriteConstraints streamWriteConstraints() {
return StreamWriteConstraints.defaults();
}
/*
/**********************************************************************
/* Public API, output configuration, state access
/**********************************************************************
*/
/**
* Accessor for context object that provides information about low-level
* logical position withing output token stream.
*<p>
* NOTE: method was called {@code getOutputContext()} in Jackson 2.x
*
* @return Stream output context ({@link TokenStreamContext}) associated with this generator
*/
public abstract TokenStreamContext streamWriteContext();
/**
* Accessor for context object provided by higher-level databinding
* functionality (or, in some cases, simple placeholder of the same)
* that allows some level of interaction including ability to trigger
* serialization of Object values through generator instance.
*
* @return Object write context ({@link ObjectWriteContext}) associated with this generator
*
* @since 3.0
*/
public abstract ObjectWriteContext objectWriteContext();
/**
* Method that can be used to get access to object that is used
* as target for generated output; this is usually either
* {@link java.io.OutputStream} or {@link java.io.Writer}, depending on what
* generator was constructed with.
* Note that returned value may be null in some cases; including
* case where implementation does not want to exposed raw
* source to caller.
* In cases where output has been decorated, object returned here
* is the decorated version; this allows some level of interaction
* between users of generator and decorator object.
*<p>
* In general use of this accessor should be considered as
* "last effort", i.e. only used if no other mechanism is applicable.
*<p>
* NOTE: was named {@code getOutputTarget()} in Jackson 2.x.
*
* @return Output target this generator was configured with
*/
public abstract Object streamWriteOutputTarget();
/**
* Method for verifying amount of content that is buffered by generator
* but not yet flushed to the underlying target (stream, writer),
* in units (byte, char) that the generator implementation uses for buffering;
* or -1 if this information is not available.
* Unit used is often the same as the unit of underlying target (that is,
* {@code byte} for {@link java.io.OutputStream},
* {@code char} for {@link java.io.Writer}),
* but may differ if buffering is done before encoding.
* Default JSON-backed implementations do use matching units.
*<p>
* NOTE: was named {@code getOutputBuffered()} in Jackson 2.x.
*
* @return Amount of content buffered in internal units, if amount known and
* accessible; -1 if not accessible.
*/
public abstract int streamWriteOutputBuffered();
/**
* Helper method, usually equivalent to:
*<code>
* getOutputContext().currentValue();
*</code>
*<p>
* Note that "current value" is NOT populated (or used) by Streaming generator;
* it is only used by higher-level data-binding functionality.
* The reason it is included here is that it can be stored and accessed hierarchically,
* and gets passed through data-binding.
*
* @return "Current value" for the current context this generator has
*/
public abstract Object currentValue();
/**
* Helper method, usually equivalent to:
*<code>
* getOutputContext().assignCurrentValue(v);
*</code>
* used to assign "current value" for the current context of this generator.
* It is usually assigned and used by higher level data-binding functionality
* (instead of streaming parsers/generators) but is stored at streaming level.
*
* @param v "Current value" to assign to the current output context of this generator
*/
public abstract void assignCurrentValue(Object v);
/*
/**********************************************************************
/* Public API, Feature configuration
/**********************************************************************
*/
// 25-Jan-2021, tatu: Still called by `ClassUtil` of jackson-databind, to
// prevent secondary issues when closing generator. Should probably figure
// out alternate means of safe closing...
/**
* Method for enabling or disabling specified feature:
* check {@link StreamWriteFeature} for list of available features.
*<p>
* NOTE: mostly left in 3.0 just to support disabling of
* {@link StreamWriteFeature#AUTO_CLOSE_CONTENT} by {@code jackson-databind}
*
* @param f Feature to enable or disable
* @param state Whether to enable the feature ({@code true}) or disable ({@code false})
*
* @return This generator, to allow call chaining
*/
public abstract JsonGenerator configure(StreamWriteFeature f, boolean state);
/**
* Method for checking whether given feature is enabled.
* Check {@link StreamWriteFeature} for list of available features.
*
* @param f Feature to check
*
* @return {@code True} if feature is enabled; {@code false} if not
*/
public abstract boolean isEnabled(StreamWriteFeature f);
/**
* Bulk access method for getting state of all standard (format-agnostic)
* {@link StreamWriteFeature}s.
*
* @return Bit mask that defines current states of all standard {@link StreamWriteFeature}s.
*
* @since 3.0
*/
public abstract int streamWriteFeatures();
/*
/**********************************************************************
/* Public API, other configuration
/**********************************************************************
*/
/**
* Method for accessing Schema that this generator uses, if any.
* Default implementation returns null.
*
* @return {@link FormatSchema} this generator is configured to use, if any; {@code null} if none
*/
public FormatSchema getSchema() { return null; }
/**
* Accessor method for testing what is the highest unescaped character
* configured for this generator. This may be either positive value
* (when escaping configuration has been set and is in effect), or
* 0 to indicate that no additional escaping is in effect.
* Some generators may not support additional escaping: for example,
* generators for binary formats that do not use escaping should
* simply return 0.
*
* @return Currently active limitation for highest non-escaped character,
* if defined; or 0 to indicate no additional escaping is performed.
*/
public int getHighestNonEscapedChar() { return 0; }
/**
* Method for accessing custom escapes generator uses for {@link JsonGenerator}s
* it creates.
*
* @return {@link CharacterEscapes} this generator is configured to use, if any; {@code null} if none
*/
public CharacterEscapes getCharacterEscapes() { return null; }
// 04-Oct-2017, tatu: Would like to remove this method, but alas JSONP-support
// does require it...
/**
* Method for defining custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*<p>
* Default implementation does nothing and simply returns this instance.
*
* @param esc {@link CharacterEscapes} to configure this generator to use, if any; {@code null} if none
*
* @return This generator, to allow call chaining
*/
public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { return this; }
/**
* Accessor for object that handles pretty-printing (usually additional white space to make
* results more human-readable) during output. If {@code null}, no pretty-printing is
* done.
* <p>
* NOTE: this may be {@link PrettyPrinter} that {@link TokenStreamFactory} was
* configured with (if stateless), OR an instance created via
* {@link tools.jackson.core.util.Instantiatable#createInstance()} (if
* stateful).
*<p>
* Default implementation returns null so pretty-printing capable generators
* need to override it.
*
* @return Pretty printer used by this generator, if any; {@code null} if none
*/
public PrettyPrinter getPrettyPrinter() { return null; }
/*
/**********************************************************************
/* Public API, capability introspection methods
/**********************************************************************
*/
/**
* Introspection method that may be called to see if the underlying
* data format supports some kind of Object Ids natively (many do not;
* for example, JSON doesn't).
* This method <b>must</b> be called prior to calling
* {@link #writeObjectId} or {@link #writeObjectRef}.
*<p>
* Default implementation returns false; overridden by data formats
* that do support native Object Ids. Caller is expected to either
* use a non-native notation (explicit property or such), or fail,
* in case it cannot use native object ids.
*
* @return {@code True} if this generator is capable of writing "native" Object Ids
* (which is typically determined by capabilities of the underlying format),
* {@code false} if not
*/
public boolean canWriteObjectId() { return false; }
/**
* Introspection method that may be called to see if the underlying
* data format supports some kind of Type Ids natively (many do not;
* for example, JSON doesn't).
* This method <b>must</b> be called prior to calling
* {@link #writeTypeId}.
*<p>
* Default implementation returns false; overridden by data formats
* that do support native Type Ids. Caller is expected to either
* use a non-native notation (explicit property or such), or fail,
* in case it cannot use native type ids.
*
* @return {@code True} if this generator is capable of writing "native" Type Ids
* (which is typically determined by capabilities of the underlying format),
* {@code false} if not
*/
public boolean canWriteTypeId() { return false; }
/**
* Introspection method to call to check whether it is ok to omit
* writing of Object properties or not. Most formats do allow omission,
* but certain positional formats (such as CSV) require output of
* place holders, even if no real values are to be emitted.
*<p>
* NOTE: in Jackson 2.x method was {@code canOmitFields()}.
*
* @return {@code True} if this generator is allowed to only write values
* of some Object properties and omit the rest; {@code false} if not
*/
public boolean canOmitProperties() { return true; }
/**
* Accessor for checking whether this generator has specified capability.
* Short-hand for:
* {@code return getWriteCapabilities().isEnabled(capability); }
*
* @param capability Capability to check
*
* @return True if this generator has specified capability; false if not
*/
public abstract boolean has(StreamWriteCapability capability);
/**
* Accessor for getting metadata on capabilities of this generator, based on
* underlying data format being read (directly or indirectly).
*
* @return Set of read capabilities for content to generate via this generator
*/
public abstract JacksonFeatureSet<StreamWriteCapability> streamWriteCapabilities();
/*
/**********************************************************************
/* Public API, write methods, structural
/**********************************************************************
*/
/**
* Method for writing starting marker of a Array value
* (for JSON this is character '['; plus possible white space decoration
* if pretty-printing is enabled).
*<p>
* Array values can be written in any context where values
* are allowed: meaning everywhere except for when
* a property name is expected.
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeStartArray() throws JacksonException;
/**
* Method for writing start marker of an Array value, similar
* to {@link #writeStartArray()}, but also specifying what is the
* Java object that the Array Object being written represents (if any);
* {@code null} may be passed if not known or not applicable.
* This value is accessible from context as "current value"
*
* @param currentValue Java Object that Array being written represents, if any
* (or {@code null} if not known or not applicable)
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*
* @return This generator, to allow call chaining
*/
public abstract JsonGenerator writeStartArray(Object currentValue) throws JacksonException;
/**
* Method for writing start marker of an Array value, similar
* to {@link #writeStartArray()}, but also specifying what is the
* Java object that the Array Object being written represents (if any)
* and how many elements will be written for the array before calling
* {@link #writeEndArray()}.
*
* @param currentValue Java Object that Array being written represents, if any
* (or {@code null} if not known or not applicable)
* @param size Number of elements this Array will have: actual
* number of values written (before matching call to
* {@link #writeEndArray()} MUST match; generator MAY verify
* this is the case (and SHOULD if format itself encodes length)
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeStartArray(Object currentValue, int size) throws JacksonException;
/**
* Method for writing closing marker of a JSON Array value
* (character ']'; plus possible white space decoration
* if pretty-printing is enabled).
*<p>
* Marker can be written if the innermost structured type is Array.
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeEndArray() throws JacksonException;
/**
* Method for writing starting marker of an Object value
* (character '{'; plus possible white space decoration
* if pretty-printing is enabled).
*<p>
* Object values can be written in any context where values
* are allowed: meaning everywhere except for when
* a property name is expected.
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeStartObject() throws JacksonException;
/**
* Method for writing starting marker of an Object value
* to represent the given Java Object value.
* Argument is offered as metadata, but more
* importantly it should be assigned as the "current value"
* for the Object content that gets constructed and initialized.
*<p>
* Object values can be written in any context where values
* are allowed: meaning everywhere except for when
* a property name is expected.
*
* @param currentValue Java Object that Object being written represents, if any
* (or {@code null} if not known or not applicable)
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeStartObject(Object currentValue) throws JacksonException;
/**
* Method for writing starting marker of an Object value
* to represent the given Java Object value.
* Argument is offered as metadata, but more
* importantly it should be assigned as the "current value"
* for the Object content that gets constructed and initialized.
* In addition, caller knows number of key/value pairs ("properties")
* that will get written for the Object value: this is relevant for
* some format backends (but not, as an example, for JSON).
*<p>
* Object values can be written in any context where values
* are allowed: meaning everywhere except for when
* a property name is expected.
*
* @param forValue Object value to be written (assigned as "current value" for
* the Object context that gets created)
* @param size Number of key/value pairs this Object will have: actual
* number of entries written (before matching call to
* {@link #writeEndObject()} MUST match; generator MAY verify
* this is the case (and SHOULD if format itself encodes length)
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeStartObject(Object forValue, int size) throws JacksonException;
/**
* Method for writing closing marker of an Object value
* (character '}'; plus possible white space decoration
* if pretty-printing is enabled).
*<p>
* Marker can be written if the innermost structured type
* is Object, and the last written event was either a
* complete value, or START-OBJECT marker (see JSON specification
* for more details).
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeEndObject() throws JacksonException;
/**
* Method for writing an Object Property name (JSON String surrounded by
* double quotes: syntactically identical to a JSON String value),
* possibly decorated by white space if pretty-printing is enabled.
*<p>
* Property names can only be written in Object context (check out
* JSON specification for details), when Object Property name is expected
* (property names alternate with values).
*
* @param name Name of the Object Property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeName(String name) throws JacksonException;
/**
* Method similar to {@link #writeName(String)}, main difference
* being that it may perform better as some of processing (such as
* quoting of certain characters, or encoding into external encoding
* if supported by generator) can be done just once and reused for
* later calls.
*<p>
* Default implementation simple uses unprocessed name container in
* serialized String; implementations are strongly encouraged to make
* use of more efficient methods argument object has.
*
* @param name Pre-encoded name of the Object Property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeName(SerializableString name) throws JacksonException;
/**
* Alternative to {@link #writeName(String)} that may be used
* in cases where Object Property key is of numeric type; usually where
* underlying format supports such notion (some binary formats do,
* unlike JSON).
* Default implementation will simply convert id into {@code String}
* and call {@link #writeName(String)}.
*
* @param id Property key id to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writePropertyId(long id) throws JacksonException;
/*
/**********************************************************************
/* Public API, write methods, scalar arrays
/**********************************************************************
*/
/**
* Value write method that can be called to write a single
* array (sequence of {@link JsonToken#START_ARRAY}, zero or
* more {@link JsonToken#VALUE_NUMBER_INT}, {@link JsonToken#END_ARRAY})
*
* @param array Array that contains values to write
* @param offset Offset of the first element to write, within array
* @param length Number of elements in array to write, from `offset` to `offset + len - 1`
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeArray(int[] array, int offset, int length) throws JacksonException
{
Objects.requireNonNull(array, "null 'array' argument");
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
return this;
}
/**
* Value write method that can be called to write a single
* array (sequence of {@link JsonToken#START_ARRAY}, zero or
* more {@link JsonToken#VALUE_NUMBER_INT}, {@link JsonToken#END_ARRAY})
*
* @param array Array that contains values to write
* @param offset Offset of the first element to write, within array
* @param length Number of elements in array to write, from `offset` to `offset + len - 1`
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeArray(long[] array, int offset, int length) throws JacksonException
{
Objects.requireNonNull(array, "null 'array' argument");
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
return this;
}
/**
* Value write method that can be called to write a single
* array (sequence of {@link JsonToken#START_ARRAY}, zero or
* more {@link JsonToken#VALUE_NUMBER_FLOAT}, {@link JsonToken#END_ARRAY})
*
* @param array Array that contains values to write
* @param offset Offset of the first element to write, within array
* @param length Number of elements in array to write, from `offset` to `offset + len - 1`
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeArray(double[] array, int offset, int length) throws JacksonException
{
Objects.requireNonNull(array, "null 'array' argument");
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
return this;
}
/**
* Value write method that can be called to write a single
* array (sequence of {@link JsonToken#START_ARRAY}, zero or
* more {@link JsonToken#VALUE_STRING}, {@link JsonToken#END_ARRAY})
*
* @param array Array that contains values to write
* @param offset Offset of the first element to write, within array
* @param length Number of elements in array to write, from `offset` to `offset + len - 1`
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeArray(String[] array, int offset, int length) throws JacksonException
{
Objects.requireNonNull(array, "null 'array' argument");
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeString(array[i]);
}
writeEndArray();
return this;
}
/*
/**********************************************************************
/* Public API, write methods, text/String values
/**********************************************************************
*/
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) property value or
* a stand-alone (root-level value) String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
*
* @param value String value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeString(String value) throws JacksonException;
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) property value or
* a stand alone String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
* If {@code len} is < 0, then write all contents of the reader.
* Otherwise, write only len characters.
*<p>
* Note: actual length of content available may exceed {@code len} but
* cannot be less than it: if not enough content available, a
* {@link StreamWriteException} will be thrown.
*
* @param reader Reader to use for reading Text value to write
* @param len Maximum Length of Text value to read (in {@code char}s, non-negative)
* if known; {@code -1} to indicate "read and write it all"
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
* (including the case where {@code reader} does not provide enough content)
*/
public abstract JsonGenerator writeString(Reader reader, int len) throws JacksonException;
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) property value or
* a stand alone String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
*
* @param buffer Buffer that contains String value to write
* @param offset Offset in {@code buffer} of the first character of String value to write
* @param len Length of the String value (in characters) to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeString(char[] buffer, int offset, int len) throws JacksonException;
/**
* Method similar to {@link #writeString(String)}, but that takes
* {@link SerializableString} which can make this potentially
* more efficient to call as generator may be able to reuse
* quoted and/or encoded representation.
*<p>
* Default implementation just calls {@link #writeString(String)};
* sub-classes should override it with more efficient implementation
* if possible.
*
* @param value Pre-encoded String value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeString(SerializableString value) throws JacksonException;
/**
* Method similar to {@link #writeString(String)} but that takes as
* its input a UTF-8 encoded String that is to be output as-is, without additional
* escaping (type of which depends on data format; backslashes for JSON).
* However, quoting that data format requires (like double-quotes for JSON) will be added
* around the value if and as necessary.
*<p>
* Note that some backends may choose not to support this method: for
* example, if underlying destination is a {@link java.io.Writer}
* using this method would require UTF-8 decoding.
* If so, implementation may instead choose to throw a
* {@link UnsupportedOperationException} due to ineffectiveness
* of having to decode input.
*
* @param buffer Buffer that contains String value to write
* @param offset Offset in {@code buffer} of the first byte of String value to write
* @param len Length of the String value (in characters) to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeRawUTF8String(byte[] buffer, int offset, int len)
throws JacksonException;
/**
* Method similar to {@link #writeString(String)} but that takes as its input
* a UTF-8 encoded String which has <b>not</b> been escaped using whatever
* escaping scheme data format requires (for JSON that is backslash-escaping
* for control characters and double-quotes; for other formats something else).
* This means that textual JSON backends need to check if value needs
* JSON escaping, but otherwise can just be copied as is to output.
* Also, quoting that data format requires (like double-quotes for JSON) will be added
* around the value if and as necessary.
*<p>
* Note that some backends may choose not to support this method: for
* example, if underlying destination is a {@link java.io.Writer}
* using this method would require UTF-8 decoding.
* In this case
* generator implementation may instead choose to throw a
* {@link UnsupportedOperationException} due to ineffectiveness
* of having to decode input.
*
* @param buffer Buffer that contains String value to write
* @param offset Offset in {@code buffer} of the first byte of String value to write
* @param len Length of the String value (in characters) to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeUTF8String(byte[] buffer, int offset, int len)
throws JacksonException;
/*
/**********************************************************************
/* Public API, write methods, raw content
/**********************************************************************
*/
/**
* Method that will force generator to copy
* input text verbatim with <b>no</b> modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*<p>
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*
* @return This generator, to allow call chaining
*
* @param text Textual contents to include as-is in output.
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeRaw(String text) throws JacksonException;
/**
* Method that will force generator to copy
* input text verbatim with <b>no</b> modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*<p>
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*
* @param text String that has contents to include as-is in output
* @param offset Offset within {@code text} of the first character to output
* @param len Length of content (from {@code text}, starting at offset {@code offset}) to output
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeRaw(String text, int offset, int len) throws JacksonException;
/**
* Method that will force generator to copy
* input text verbatim with <b>no</b> modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*<p>
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*
* @param buffer Buffer that has contents to include as-is in output
* @param offset Offset within {@code text} of the first character to output
* @param len Length of content (from {@code text}, starting at offset {@code offset}) to output
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeRaw(char[] buffer, int offset, int len) throws JacksonException;
/**
* Method that will force generator to copy
* input text verbatim with <b>no</b> modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*<p>
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*
* @param c Character to included in output
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeRaw(char c) throws JacksonException;
/**
* Method that will force generator to copy
* input text verbatim with <b>no</b> modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*<p>
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*<p>
* The default implementation delegates to {@link #writeRaw(String)};
* other backends that support raw inclusion of text are encouraged
* to implement it in more efficient manner (especially if they
* use UTF-8 encoding).
*
* @param raw Pre-encoded textual contents to included in output
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeRaw(SerializableString raw) throws JacksonException {
return writeRaw(raw.getValue());
}
/**
* Method that will force generator to copy
* input text verbatim without any modifications, but assuming
* it must constitute a single legal JSON value (number, string,
* boolean, null, Array or List). Assuming this, proper separators
* are added if and as needed (comma or colon), and generator
* state updated to reflect this.
*
* @param text Textual contents to included in output
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeRawValue(String text) throws JacksonException;
public abstract JsonGenerator writeRawValue(String text, int offset, int len) throws JacksonException;
public abstract JsonGenerator writeRawValue(char[] text, int offset, int len) throws JacksonException;
/**
* Method similar to {@link #writeRawValue(String)}, but potentially more
* efficient as it may be able to use pre-encoded content (similar to
* {@link #writeRaw(SerializableString)}.
*
* @param raw Pre-encoded textual contents to included in output
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeRawValue(SerializableString raw) throws JacksonException {
return writeRawValue(raw.getValue());
}
/*
/**********************************************************************
/* Public API, write methods, Binary values
/**********************************************************************
*/
/**
* Method that will output given chunk of binary data as base64
* encoded, as a complete String value (surrounded by double quotes).
* This method defaults
*<p>
* Note: because JSON Strings cannot contain unescaped linefeeds,
* if linefeeds are included (as per last argument), they must be
* escaped. This adds overhead for decoding without improving
* readability.
* Alternatively if linefeeds are not included,
* resulting String value may violate the requirement of base64
* RFC which mandates line-length of 76 characters and use of
* linefeeds. However, all {@link JsonParser} implementations
* are required to accept such "long line base64"; as do
* typical production-level base64 decoders.
*
* @param bv Base64 variant to use: defines details such as
* whether padding is used (and if so, using which character);
* what is the maximum line length before adding linefeed,
* and also the underlying alphabet to use.
* @param data Buffer that contains binary data to write
* @param offset Offset in {@code data} of the first byte of data to write
* @param len Length of data to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeBinary(Base64Variant bv,
byte[] data, int offset, int len) throws JacksonException;
/**
* Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}).
*
* @param data Buffer that contains binary data to write
* @param offset Offset in {@code data} of the first byte of data to write
* @param len Length of data to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeBinary(byte[] data, int offset, int len) throws JacksonException {
return writeBinary(Base64Variants.getDefaultVariant(), data, offset, len);
}
/**
* Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but assumes default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}). Also
* assumes that whole byte array is to be output.
*
* @param data Buffer that contains binary data to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeBinary(byte[] data) throws JacksonException {
return writeBinary(Base64Variants.getDefaultVariant(), data, 0, data.length);
}
/**
* Similar to {@link #writeBinary(Base64Variant,InputStream,int)},
* but assumes default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}).
*
* @param data InputStream to use for reading binary data to write.
* Will not be closed after successful write operation
* @param dataLength (optional) number of bytes that will be available;
* or -1 to be indicate it is not known. Note that implementations
* need not support cases where length is not known in advance; this
* depends on underlying data format: JSON output does NOT require length,
* other formats may
*
* @return Number of bytes actually written
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public int writeBinary(InputStream data, int dataLength) throws JacksonException {
return writeBinary(Base64Variants.getDefaultVariant(), data, dataLength);
}
/**
* Method similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but where input is provided through a stream, allowing for incremental
* writes without holding the whole input in memory.
*
* @param bv Base64 variant to use
* @param data InputStream to use for reading binary data to write.
* Will not be closed after successful write operation
* @param dataLength (optional) number of bytes that will be available;
* or -1 to be indicate it is not known.
* If a positive length is given, <code>data</code> MUST provide at least
* that many bytes: if not, an exception will be thrown.
* Note that implementations
* need not support cases where length is not known in advance; this
* depends on underlying data format: JSON output does NOT require length,
* other formats may.
*
* @return Number of bytes read from <code>data</code> and written as binary payload
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract int writeBinary(Base64Variant bv,
InputStream data, int dataLength) throws JacksonException;
/*
/**********************************************************************
/* Public API, write methods, numeric
/**********************************************************************
*/
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNumber(short v) throws JacksonException;
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNumber(int v) throws JacksonException;
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNumber(long v) throws JacksonException;
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNumber(BigInteger v) throws JacksonException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNumber(double v) throws JacksonException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNumber(float v) throws JacksonException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNumber(BigDecimal v) throws JacksonException;
/**
* Write method that can be used for custom numeric types that can
* not be (easily?) converted to "standard" Java number types; or, for
* special formatting with formats that allow this (CSV).
* Because numbers are not surrounded by double quotes, regular
* {@link #writeString} method cannot be used; nor
* {@link #writeRaw} because that does not properly handle
* value separators needed in Array or Object contexts.
*<p>
* Note: because of lack of type safety, some generator
* implementations will not be able to implement this method.
* For example, some binary formats require type information for
* encoding; similarly for generator-wrappers around Java objects or {@link TreeNode}s.
* If implementation does not implement this method,
* it should a {@link StreamWriteException}).
*
* @param encodedValue Textual (possibly formatted) number representation to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream, for example
* if underlying data format does not support numbers serialized textually (especially
* Schema-based formats that require actual number type)
*/
public abstract JsonGenerator writeNumber(String encodedValue) throws JacksonException;
/**
* Overloaded version of {@link #writeNumber(String)} with same semantics
* but possibly more efficient operation.
*
* @param encodedValueBuffer Buffer that contains the textual number representation to write
* @param offset Offset of the first character of value to write
* @param len Length of the value (in characters) to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeNumber(char[] encodedValueBuffer, int offset, int len) throws JacksonException {
return writeNumber(new String(encodedValueBuffer, offset, len));
}
/*
/**********************************************************************
/* Public API, write methods, other value types
/**********************************************************************
*/
/**
* Method for outputting literal JSON boolean value (one of
* Strings 'true' and 'false').
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param state Boolean value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeBoolean(boolean state) throws JacksonException;
/**
* Method for outputting literal JSON null value.
* Can be called in any context where a value is expected
* (Array value, Object property value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeNull() throws JacksonException;
/**
* Method that can be called on backends that support passing opaque native
* values that some data formats support; not used with JSON backend,
* more common with binary formats.
*<p>
* NOTE: this is NOT the method to call for serializing regular POJOs,
* see {@link #writePOJO} instead.
*
* @param object Native format-specific value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeEmbeddedObject(Object object) throws JacksonException {
// 01-Sep-2016, tatu: As per [core#318], handle small number of cases
if (object == null) {
writeNull();
return this;
}
if (object instanceof byte[]) {
writeBinary((byte[]) object);
return this;
}
throw _constructWriteException("No native support for writing embedded objects of type %s",
object.getClass().getName());
}
/*
/**********************************************************************
/* Public API, write methods, Native Ids (type, object)
/**********************************************************************
*/
/**
* Method that can be called to output so-called native Object Id.
* Note that it may only be called after ensuring this is legal
* (with {@link #canWriteObjectId()}), as not all data formats
* have native type id support; and some may only allow them in
* certain positions or locations.
*
* @param id Native Object Id to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream;
* typically if Object ID output is not allowed
* (either at all, or specifically in this position in output)
*/
public JsonGenerator writeObjectId(Object id) throws JacksonException {
throw _constructWriteException("No native support for writing Object Ids");
}
/**
* Method that can be called to output references to native Object Ids.
* Note that it may only be called after ensuring this is legal
* (with {@link #canWriteObjectId()}), as not all data formats
* have native type id support; and some may only allow them in
* certain positions or locations.
* If output is not allowed by the data format in this position,
* a {@link StreamWriteException} will be thrown.
*
* @param referenced Referenced value, for which Object Id is expected to be written
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream;
* typically if Object ID output is not allowed
* (either at all, or specifically in this position in output)
*/
public JsonGenerator writeObjectRef(Object referenced) throws JacksonException {
throw _constructWriteException("No native support for writing Object Ids");
}
/**
* Method that can be called to output so-called native Type Id.
* Note that it may only be called after ensuring this is legal
* (with {@link #canWriteTypeId()}), as not all data formats
* have native type id support; and some may only allow them in
* certain positions or locations.
* If output is not allowed by the data format in this position,
* a {@link StreamWriteException} will be thrown.
*
* @param id Native Type Id to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeTypeId(Object id) throws JacksonException {
throw _constructWriteException("No native support for writing Type Ids");
}
/**
* Replacement method for {@link #writeTypeId(Object)} which is called
* regardless of whether format has native type ids. If it does have native
* type ids, those are to be used (if configuration allows this), if not,
* structural type id inclusion is to be used. For JSON, for example, no
* native type ids exist and structural inclusion is always used.
*<p>
* NOTE: databind may choose to skip calling this method for some special cases
* (and instead included type id via regular write methods and/or {@link #writeTypeId}
* -- this is discouraged, but not illegal, and may be necessary as a work-around
* in some cases.
*
* @param typeIdDef Full Type Id definition
*
* @return {@link WritableTypeId} for caller to retain and pass to matching
* {@link #writeTypeSuffix} call
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef)
throws JacksonException
{
final boolean wasStartObjectWritten = canWriteTypeId()
? _writeTypePrefixUsingNative(typeIdDef)
: _writeTypePrefixUsingWrapper(typeIdDef);
// And then possible start marker for value itself:
switch (typeIdDef.valueShape) {
case START_OBJECT:
if (!wasStartObjectWritten) {
writeStartObject(typeIdDef.forValue);
}
break;
case START_ARRAY:
writeStartArray(typeIdDef.forValue);
break;
default: // otherwise: no start marker
}
return typeIdDef;
}
/**
* Writes a native type id (when supported by format).
*
* @return True if start of an object has been written, False otherwise.
*/
protected boolean _writeTypePrefixUsingNative(WritableTypeId typeIdDef) throws JacksonException {
typeIdDef.wrapperWritten = false;
writeTypeId(typeIdDef.id);
return false;
}
/**
* Writes a wrapper for the type id if necessary.
*
* @return True if start of an object has been written, false otherwise.
*/
protected boolean _writeTypePrefixUsingWrapper(WritableTypeId typeIdDef) throws JacksonException {
// Normally we only support String type ids (non-String reserved for native type ids)
final String id = Objects.toString(typeIdDef.id, null);
// If we don't have Type ID we don't write a wrapper.
if (id == null) {
return false;
}
Inclusion incl = typeIdDef.include;
// first: cannot output "as property" if value not Object; if so, must do "as array"
if ((typeIdDef.valueShape != JsonToken.START_OBJECT) && incl.requiresObjectContext()) {
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
}
typeIdDef.wrapperWritten = true;
switch (incl) {
case PARENT_PROPERTY:
// nothing to do here, as it has to be written in suffix...
break;
case PAYLOAD_PROPERTY:
// only output as native type id; otherwise caller must handle using some
// other mechanism, so...
break;
case METADATA_PROPERTY:
// must have Object context by now, so simply write as field name
// Note, too, that it's bit tricky, since we must print START_OBJECT that is part
// of value first -- and then NOT output it later on: hence return "early"
writeStartObject(typeIdDef.forValue);
writeStringProperty(typeIdDef.asProperty, id);
return true;
case WRAPPER_OBJECT:
// NOTE: this is wrapper, not directly related to value to output, so
// do NOT pass "typeIdDef.forValue"
writeStartObject();
writeName(id);
break;
case WRAPPER_ARRAY:
default: // should never occur but translate as "as-array"
writeStartArray(); // wrapper, not actual array object to write
writeString(id);
}
return false;
}
public WritableTypeId writeTypeSuffix(WritableTypeId typeIdDef) throws JacksonException
{
final JsonToken valueShape = typeIdDef.valueShape;
// First: does value need closing?
if (valueShape == JsonToken.START_OBJECT) {
writeEndObject();
} else if (valueShape == JsonToken.START_ARRAY) {
writeEndArray();
}
if (typeIdDef.wrapperWritten) {
switch (typeIdDef.include) {
case WRAPPER_ARRAY:
writeEndArray();
break;
case PARENT_PROPERTY:
// unusually, need to output AFTER value. And no real wrapper...
{
Object id = typeIdDef.id;
String idStr = (id instanceof String) ? (String) id : String.valueOf(id);
writeStringProperty(typeIdDef.asProperty, idStr);
}
break;
case METADATA_PROPERTY:
case PAYLOAD_PROPERTY:
// no actual wrapper; included within Object itself
break;
case WRAPPER_OBJECT:
default: // should never occur but...
writeEndObject();
break;
}
}
return typeIdDef;
}
/*
/**********************************************************************
/* Public API, write methods, serializing Java objects
/**********************************************************************
*/
/**
* Method for writing given Java object (POJO) as tokens into
* stream this generator manages; serialization must be a valid JSON Value
* (Object, Array, null, Number, String or Boolean).
* This is done by delegating call to
* {@link ObjectWriteContext#writeValue(JsonGenerator, Object)}.
*<p>
* NOTE: this method does not trigger flushing of the generator (none of
* {@link JsonGenerator} {@code writeXxx} methods do) in Jackson 3.x:
* this is different from behavior in Jackson 2.x where a side effect
* of delegation to {@code ObjectMapper} caused flushing to occur.
*
* @param pojo Java Object (POJO) value to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writePOJO(Object pojo) throws JacksonException;
/**
* Method for writing given JSON tree (expressed as a tree
* where given {@code TreeNode} is the root) using this generator.
* This is done by delegating call to
* {@link ObjectWriteContext#writeTree}.
*<p>
* NOTE: this method does not trigger flushing of the generator (none of
* {@link JsonGenerator} {@code writeXxx} methods do) in Jackson 3.x:
* this is different from behavior in Jackson 2.x where a side effect
* of delegation to {@code ObjectMapper} caused flushing to occur.
*
* @param rootNode {@link TreeNode} to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public abstract JsonGenerator writeTree(TreeNode rootNode) throws JacksonException;
/*
/**********************************************************************
/* Public API, convenience property write methods
/**********************************************************************
*/
// 25-May-2020, tatu: NOTE! Made `final` on purpose in 3.x to prevent issues
// rising from complexity of overriding only some of methods (writeName()
// and matching writeXxx() for value)
/**
* Convenience method for outputting an Object property
* that contains specified data in base64-encoded form.
* Equivalent to:
*<pre>
* writeName(propertyName);
* writeBinary(value);
*</pre>
*
* @param propertyName Name of Object Property to write
* @param data Binary value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeBinaryProperty(String propertyName, byte[] data) throws JacksonException {
writeName(propertyName);
return writeBinary(data);
}
/**
* Convenience method for outputting an Object property
* that has a boolean value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeBoolean(value);
*</pre>
*
* @param propertyName Name of Object Property to write
* @param value Boolean value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeBooleanProperty(String propertyName, boolean value) throws JacksonException {
writeName(propertyName);
return writeBoolean(value);
}
/**
* Convenience method for outputting an Object property
* that has JSON literal value null. Equivalent to:
*<pre>
* writeName(propertyName);
* writeNull();
*</pre>
*
* @param propertyName Name of the null-valued property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNullProperty(String propertyName) throws JacksonException {
writeName(propertyName);
return writeNull();
}
/**
* Convenience method for outputting an Object property
* that has a String value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeString(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value String value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeStringProperty(String propertyName, String value) throws JacksonException {
writeName(propertyName);
return writeString(value);
}
/**
* Convenience method for outputting an Object property
* that has the specified numeric value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeNumber(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value Numeric value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNumberProperty(String propertyName, short value) throws JacksonException {
writeName(propertyName);
return writeNumber(value);
}
/**
* Convenience method for outputting an Object property
* that has the specified numeric value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeNumber(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value Numeric value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNumberProperty(String propertyName, int value) throws JacksonException {
writeName(propertyName);
return writeNumber(value);
}
/**
* Convenience method for outputting an Object property
* that has the specified numeric value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeNumber(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value Numeric value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNumberProperty(String propertyName, long value) throws JacksonException {
writeName(propertyName);
return writeNumber(value);
}
/**
* Convenience method for outputting an Object property
* that has the specified numeric value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeNumber(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value Numeric value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNumberProperty(String propertyName, BigInteger value) throws JacksonException {
writeName(propertyName);
return writeNumber(value);
}
/**
* Convenience method for outputting an Object property
* that has the specified numeric value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeNumber(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value Numeric value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNumberProperty(String propertyName, float value) throws JacksonException {
writeName(propertyName);
return writeNumber(value);
}
/**
* Convenience method for outputting an Object property
* that has the specified numeric value. Equivalent to:
*<pre>
* writeName(propertyName);
* writeNumber(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value Numeric value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNumberProperty(String propertyName, double value) throws JacksonException {
writeName(propertyName);
return writeNumber(value);
}
/**
* Convenience method for outputting an Object property
* that has the specified numeric value.
* Equivalent to:
*<pre>
* writeName(propertyName);
* writeNumber(value);
*</pre>
*
* @param propertyName Name of the property to write
* @param value Numeric value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeNumberProperty(String propertyName, BigDecimal value) throws JacksonException {
writeName(propertyName);
return writeNumber(value);
}
/**
* Convenience method for outputting an Object property
* (that will contain a JSON Array value), and the START_ARRAY marker.
* Equivalent to:
*<pre>
* writeName(propertyName);
* writeStartArray();
*</pre>
*<p>
* Note: caller still has to take care to close the array
* (by calling {#link #writeEndArray}) after writing all values
* of the value Array.
*
* @param propertyName Name of the Array property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeArrayPropertyStart(String propertyName) throws JacksonException {
writeName(propertyName);
return writeStartArray();
}
/**
* Convenience method for outputting an Object property
* (that will contain an Object value), and the START_OBJECT marker.
* Equivalent to:
*<pre>
* writeName(propertyName);
* writeStartObject();
*</pre>
*<p>
* Note: caller still has to take care to close the Object
* (by calling {#link #writeEndObject}) after writing all
* entries of the value Object.
*
* @param propertyName Name of the Object property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writeObjectPropertyStart(String propertyName) throws JacksonException {
writeName(propertyName);
return writeStartObject();
}
/**
* Convenience method for outputting am Object property
* that has contents of specific Java object (POJO) as its value.
* Equivalent to:
*<pre>
* writeName(propertyName);
* writeObject(pojo);
*</pre>
*<p>
* NOTE: see {@link #writePOJO(Object)} for details on how POJO value actually
* gets written (uses delegation).
*
* @param propertyName Name of the property to write
* @param pojo POJO value of the property to write
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public final JsonGenerator writePOJOProperty(String propertyName, Object pojo) throws JacksonException {
writeName(propertyName);
return writePOJO(pojo);
}
// // // But this method does need to be delegate so...
/**
* Method called to indicate that a property in this position was
* skipped. It is usually only called for generators that return
* <code>false</code> from {@link #canOmitProperties()}.
*<p>
* Default implementation does nothing; method is overriden by some format
* backends.
*
* @param propertyName Name of the property that is being omitted
*
* @return This generator, to allow call chaining
*
* @throws JacksonIOException if there is an underlying I/O problem
* @throws StreamWriteException for problems in encoding token stream
*/
public JsonGenerator writeOmittedProperty(String propertyName) throws JacksonException {
return this;
}
/*
/**********************************************************************
/* Public API, copy-through methods
/*
/* NOTE: need to remain here for `JsonGeneratorDelegate` to call
/* (or refactor to have "JsonGeneratorMinimalBase" or such)
/**********************************************************************
*/
/**
* Method for copying contents of the current event that
* the given parser instance points to.
* Note that the method <b>will not</b> copy any other events,
* such as events contained within JSON Array or Object structures.
*<p>
* Calling this method will not advance the given
* parser, although it may cause parser to internally process
* more data (if it lazy loads contents of value events, for example)
*
* @param p Parser that points to the event to copy
*
* @throws JacksonIOException if there is an underlying I/O problem (reading or writing)
* @throws StreamReadException for problems with decoding of token stream
* @throws StreamWriteException for problems in encoding token stream
*/
public void copyCurrentEvent(JsonParser p) throws JacksonException
{
JsonToken t = p.currentToken();
final int token = (t == null) ? ID_NOT_AVAILABLE : t.id();
switch (token) {
case ID_NOT_AVAILABLE:
_reportError("No current event to copy");
break; // never gets here
case ID_START_OBJECT:
writeStartObject();
break;
case ID_END_OBJECT:
writeEndObject();
break;
case ID_START_ARRAY:
writeStartArray();
break;
case ID_END_ARRAY:
writeEndArray();
break;
case ID_PROPERTY_NAME:
writeName(p.currentName());
break;
case ID_STRING:
_copyCurrentStringValue(p);
break;
case ID_NUMBER_INT:
_copyCurrentIntValue(p);
break;
case ID_NUMBER_FLOAT:
// Different from "copyCurrentEventExact"!
_copyCurrentFloatValue(p);
break;
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writePOJO(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
/**
* Same as {@link #copyCurrentEvent} with the exception that copying of numeric
* values tries to avoid any conversion losses; in particular for floating-point
* numbers. This usually matters when transcoding from textual format like JSON
* to a binary format.
* See {@link #_copyCurrentFloatValueExact} for details.
*
* @param p Parser that points to the event to copy
*
* @throws JacksonIOException if there is an underlying I/O problem (reading or writing)
* @throws StreamReadException for problems with decoding of token stream
* @throws StreamWriteException for problems in encoding token stream
*/
public void copyCurrentEventExact(JsonParser p) throws JacksonException
{
JsonToken t = p.currentToken();
final int token = (t == null) ? ID_NOT_AVAILABLE : t.id();
switch (token) {
case ID_NOT_AVAILABLE:
_reportError("No current event to copy");
break; // never gets here
case ID_START_OBJECT:
writeStartObject();
break;
case ID_END_OBJECT:
writeEndObject();
break;
case ID_START_ARRAY:
writeStartArray();
break;
case ID_END_ARRAY:
writeEndArray();
break;
case ID_PROPERTY_NAME:
writeName(p.currentName());
break;
case ID_STRING:
_copyCurrentStringValue(p);
break;
case ID_NUMBER_INT:
_copyCurrentIntValue(p);
break;
case ID_NUMBER_FLOAT:
// Different from "copyCurrentEvent"!
_copyCurrentFloatValueExact(p);
break;
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writePOJO(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
/**
* Method for copying contents of the current event
* <b>and following events that it encloses</b>
* the given parser instance points to.
*<p>
* So what constitutes enclosing? Here is the list of
* events that have associated enclosed events that will
* get copied:
*<ul>
* <li>{@link JsonToken#START_OBJECT}:
* all events up to and including matching (closing)
* {@link JsonToken#END_OBJECT} will be copied
* </li>
* <li>{@link JsonToken#START_ARRAY}
* all events up to and including matching (closing)
* {@link JsonToken#END_ARRAY} will be copied
* </li>
* <li>{@link JsonToken#PROPERTY_NAME} the logical value (which
* can consist of a single scalar value; or a sequence of related
* events for structured types (JSON Arrays, Objects)) will
* be copied along with the name itself. So essentially the
* whole <b>Object property</b> (name and value) will be copied.
* </li>
*</ul>
*<p>
* After calling this method, parser will point to the
* <b>last event</b> that was copied. This will either be
* the event parser already pointed to (if there were no
* enclosed events), or the last enclosed event copied.
*
* @param p Parser that points to the value to copy
*
* @throws JacksonIOException if there is an underlying I/O problem (reading or writing)
* @throws StreamReadException for problems with decoding of token stream
* @throws StreamWriteException for problems in encoding token stream
*/
public void copyCurrentStructure(JsonParser p) throws JacksonException
{
JsonToken t = p.currentToken();
// Let's handle property-name separately first
int id = (t == null) ? ID_NOT_AVAILABLE : t.id();
if (id == ID_PROPERTY_NAME) {
writeName(p.currentName());
t = p.nextToken();
id = (t == null) ? ID_NOT_AVAILABLE : t.id();
// fall-through to copy the associated value
}
switch (id) {
case ID_START_OBJECT:
writeStartObject();
_copyCurrentContents(p);
return;
case ID_START_ARRAY:
writeStartArray();
_copyCurrentContents(p);
return;
default:
copyCurrentEvent(p);
}
}
/**
* Same as {@link #copyCurrentStructure} with the exception that copying of numeric
* values tries to avoid any conversion losses; in particular for floating-point
* numbers. This usually matters when transcoding from textual format like JSON
* to a binary format.
* See {@link #_copyCurrentFloatValueExact} for details.
*
* @param p Parser that points to the value to copy
*
* @throws JacksonIOException if there is an underlying I/O problem (reading or writing)
* @throws StreamReadException for problems with decoding of token stream
* @throws StreamWriteException for problems in encoding token stream
*/
public void copyCurrentStructureExact(JsonParser p) throws JacksonException
{
JsonToken t = p.currentToken();
// Let's handle property-name separately first
int id = (t == null) ? ID_NOT_AVAILABLE : t.id();
if (id == ID_PROPERTY_NAME) {
writeName(p.currentName());
t = p.nextToken();
id = (t == null) ? ID_NOT_AVAILABLE : t.id();
// fall-through to copy the associated value
}
switch (id) {
case ID_START_OBJECT:
writeStartObject();
_copyCurrentContentsExact(p);
return;
case ID_START_ARRAY:
writeStartArray();
_copyCurrentContentsExact(p);
return;
default:
copyCurrentEventExact(p);
}
}
protected void _copyCurrentContents(JsonParser p) throws JacksonException
{
int depth = 1;
JsonToken t;
// Mostly copied from `copyCurrentEvent()`, but with added nesting counts
while ((t = p.nextToken()) != null) {
switch (t.id()) {
case ID_PROPERTY_NAME:
writeName(p.currentName());
break;
case ID_START_ARRAY:
writeStartArray();
++depth;
break;
case ID_START_OBJECT:
writeStartObject();
++depth;
break;
case ID_END_ARRAY:
writeEndArray();
if (--depth == 0) {
return;
}
break;
case ID_END_OBJECT:
writeEndObject();
if (--depth == 0) {
return;
}
break;
case ID_STRING:
_copyCurrentStringValue(p);
break;
case ID_NUMBER_INT:
_copyCurrentIntValue(p);
break;
case ID_NUMBER_FLOAT:
_copyCurrentFloatValue(p);
break;
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writePOJO(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
}
protected void _copyCurrentContentsExact(JsonParser p) throws JacksonException
{
int depth = 1;
JsonToken t;
// Mostly copied from `copyCurrentEventExact()`, but with added nesting counts
while ((t = p.nextToken()) != null) {
switch (t.id()) {
case ID_PROPERTY_NAME:
writeName(p.currentName());
break;
case ID_START_ARRAY:
writeStartArray();
++depth;
break;
case ID_START_OBJECT:
writeStartObject();
++depth;
break;
case ID_END_ARRAY:
writeEndArray();
if (--depth == 0) {
return;
}
break;
case ID_END_OBJECT:
writeEndObject();
if (--depth == 0) {
return;
}
break;
case ID_STRING:
_copyCurrentStringValue(p);
break;
case ID_NUMBER_INT:
_copyCurrentIntValue(p);
break;
case ID_NUMBER_FLOAT:
_copyCurrentFloatValueExact(p);
break;
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writePOJO(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
}
/**
* Method for copying current {@link JsonToken#VALUE_NUMBER_FLOAT} value;
* overridable by format backend implementations.
* Implementation checks
* {@link JsonParser#getNumberType()} for declared type and uses matching
* accessors: this may cause inexact conversion for some textual formats
* (depending on settings). If this is problematic, use
* {@link #_copyCurrentFloatValueExact} instead (note that doing so may add
* overhead).
*
* @param p Parser that points to the value to copy
*/
protected void _copyCurrentFloatValue(JsonParser p) throws JacksonException
{
NumberType t = p.getNumberType();
if (t == NumberType.BIG_DECIMAL) {
writeNumber(p.getDecimalValue());
} else if (t == NumberType.FLOAT) {
writeNumber(p.getFloatValue());
} else {
writeNumber(p.getDoubleValue());
}
}
/**
* Method for copying current {@link JsonToken#VALUE_NUMBER_FLOAT} value;
* overridable by format backend implementations.
* Implementation ensures it uses most accurate accessors necessary to retain
* exact value in case of possible numeric conversion: in practice this means
* that {@link BigDecimal} is usually used as the representation accessed from
* {@link JsonParser}, regardless of whether {@link Double} might be accurate
* (since detecting lossy conversion is not possible to do efficiently).
* If minimal overhead is desired, use {@link #_copyCurrentFloatValue} instead.
*
* @param p Parser that points to the value to copy
*
* @since 2.15
*/
protected void _copyCurrentFloatValueExact(JsonParser p) throws JacksonException
{
Number n = p.getNumberValueExact();
if (n instanceof BigDecimal) {
writeNumber((BigDecimal) n);
} else if (n instanceof Double) {
writeNumber(n.doubleValue());
} else {
writeNumber(n.floatValue());
}
}
/**
* Method for copying current {@link JsonToken#VALUE_NUMBER_FLOAT} value;
* overridable by format backend implementations.
*
* @param p Parser that points to the value to copy
*/
protected void _copyCurrentIntValue(JsonParser p) throws JacksonException
{
NumberType n = p.getNumberType();
if (n == NumberType.INT) {
writeNumber(p.getIntValue());
} else if (n == NumberType.LONG) {
writeNumber(p.getLongValue());
} else {
writeNumber(p.getBigIntegerValue());
}
}
/**
* Method for copying current {@link JsonToken#VALUE_STRING} value;
* overridable by format backend implementations.
*
* @param p Parser that points to the value to copy
*/
protected void _copyCurrentStringValue(JsonParser p) throws JacksonException
{
if (p.hasStringCharacters()) {
writeString(p.getStringCharacters(), p.getStringOffset(), p.getStringLength());
} else {
writeString(p.getString());
}
}
/*
/**********************************************************************
/* Public API, buffer handling
/**********************************************************************
*/
/**
* Method called to flush any buffered content to the underlying
* target (output stream, writer), and to flush the target itself
* as well.
*/
@Override
public abstract void flush();
/**
* Method that can be called to determine whether this generator
* is closed or not. If it is closed, no more output can be done.
*
* @return {@code True} if this generator has been closed; {@code false} if not
*/
public abstract boolean isClosed();
/*
/**********************************************************************
/* Closeable implementation
/**********************************************************************
*/
/**
* Method called to close this generator, so that no more content
* can be written.
*<p>
* Whether the underlying target (stream, writer) gets closed depends
* on whether this generator either manages the target (i.e. is the
* only one with access to the target -- case if caller passes a
* reference to the resource such as File, but not stream); or
* has feature {@link StreamWriteFeature#AUTO_CLOSE_TARGET} enabled.
* If either of above is true, the target is also closed. Otherwise
* (not managing, feature not enabled), target is not closed.
*/
@Override
public abstract void close();
/*
/**********************************************************************
/* Helper methods for sub-classes
/**********************************************************************
*/
/**
* Helper method used for constructing and throwing
* {@link StreamWriteException} with given message.
*<p>
* Note that sub-classes may override this method to add more detail
* or use a {@link StreamWriteException} sub-class.
*
* @param <T> Bogus type parameter to "return anything" so that compiler
* won't complain when chaining calls
*
* @param msg Message to construct exception with
*
* @return Does not return at all as exception is always thrown, but nominally returns "anything"
*
* @throws StreamWriteException that was constructed with given message
*/
protected <T> T _reportError(String msg) throws StreamWriteException {
throw _constructWriteException(msg);
}
protected <T> T _reportUnsupportedOperation() {
return _reportUnsupportedOperation("Operation not supported by `JsonGenerator` of type "+getClass().getName());
}
protected <T> T _reportUnsupportedOperation(String msg) {
throw new UnsupportedOperationException(msg);
}
/**
* Helper method used for constructing and throwing
* {@link StreamWriteException} with given message, in cases where
* argument(s) used for a call (usually one of {@code writeXxx()} methods)
* is invalid.
* Default implementation simply delegates to {@link #_reportError(String)}.
*
* @param <T> Bogus type parameter to "return anything" so that compiler
* won't complain when chaining calls
*
* @param msg Message to construct exception with
*
* @return Does not return at all as exception is always thrown, but nominally returns "anything"
*
* @throws StreamWriteException that was constructed with given message
*/
protected <T> T _reportArgumentError(String msg) throws StreamWriteException {
return _reportError(msg);
}
// @since 3.0
protected StreamWriteException _constructWriteException(String msg) {
return new StreamWriteException(this, msg);
}
protected StreamWriteException _constructWriteException(String msg, Object arg) {
return _constructWriteException(String.format(msg, arg));
}
protected StreamWriteException _constructWriteException(String msg, Object arg1, Object arg2) {
return _constructWriteException(String.format(msg, arg1, arg2));
}
protected StreamWriteException _constructWriteException(String msg, Throwable t) {
return new StreamWriteException(this, msg, t);
}
// @since 3.0
protected JacksonException _wrapIOFailure(IOException e) {
return JacksonIOException.construct(e, this);
}
protected final void _verifyOffsets(int arrayLength, int offset, int length)
{
if ((offset < 0) || (offset + length) > arrayLength) {
throw new IllegalArgumentException(String.format(
"invalid argument(s) (offset=%d, length=%d) for input array of %d element",
offset, length, arrayLength));
}
}
}