blob: feac224c3293f244157ca968a2761637e5b9c5c6 [file] [log] [blame]
/* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import com.fasterxml.jackson.core.JsonParser.NumberType;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.core.type.WritableTypeId.Inclusion;
import com.fasterxml.jackson.core.util.VersionUtil;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* Base class that defines public API for writing JSON content.
* Instances are created using factory methods of
* a {@link JsonFactory} instance.
*
* @author Tatu Saloranta
*/
public abstract class JsonGenerator
implements Closeable, Flushable, Versioned
{
/**
* Enumeration that defines all togglable features for generators.
*/
public enum Feature {
// // Low-level I/O / content features
/**
* Feature that determines whether generator will automatically
* close underlying output target that is NOT owned by the
* generator.
* If disabled, calling application has to separately
* close the underlying {@link OutputStream} and {@link Writer}
* instances used to create the generator. If enabled, generator
* will handle closing, as long as generator itself gets closed:
* this happens when end-of-input is encountered, or generator
* is closed by a call to {@link JsonGenerator#close}.
*<p>
* Feature is enabled by default.
*/
AUTO_CLOSE_TARGET(true),
/**
* Feature that determines what happens when the generator is
* closed while there are still unmatched
* {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT}
* entries in output content. If enabled, such Array(s) and/or
* Object(s) are automatically closed; if disabled, nothing
* specific is done.
*<p>
* Feature is enabled by default.
*/
AUTO_CLOSE_JSON_CONTENT(true),
/**
* Feature that specifies that calls to {@link #flush} will cause
* matching <code>flush()</code> to underlying {@link OutputStream}
* or {@link Writer}; if disabled this will not be done.
* Main reason to disable this feature is to prevent flushing at
* generator level, if it is not possible to prevent method being
* called by other code (like <code>ObjectMapper</code> or third
* party libraries).
*<p>
* Feature is enabled by default.
*/
FLUSH_PASSED_TO_STREAM(true),
// // Quoting-related features
/**
* Feature that determines whether JSON Object field names are
* quoted using double-quotes, as specified by JSON specification
* or not. Ability to disable quoting was added to support use
* cases where they are not usually expected, which most commonly
* occurs when used straight from Javascript.
*<p>
* Feature is enabled by default (since it is required by JSON specification).
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#QUOTE_FIELD_NAMES} instead
*/
@Deprecated
QUOTE_FIELD_NAMES(true),
/**
* Feature that determines whether "exceptional" (not real number)
* float/double values are output as quoted strings.
* The values checked are Double.Nan,
* Double.POSITIVE_INFINITY and Double.NEGATIVE_INIFINTY (and
* associated Float values).
* If feature is disabled, these numbers are still output using
* associated literal values, resulting in non-conformant
* output.
*<p>
* Feature is enabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NAN_AS_STRINGS} instead
*/
@Deprecated
QUOTE_NON_NUMERIC_NUMBERS(true),
// // Character escaping features
/**
* Feature that specifies that all characters beyond 7-bit ASCII
* range (i.e. code points of 128 and above) need to be output
* using format-specific escapes (for JSON, backslash escapes),
* if format uses escaping mechanisms (which is generally true
* for textual formats but not for binary formats).
*<p>
* Note that this setting may not necessarily make sense for all
* data formats (for example, binary formats typically do not use
* any escaping mechanisms; and some textual formats do not have
* general-purpose escaping); if so, settings is simply ignored.
* Put another way, effects of this feature are data-format specific.
*<p>
* Feature is disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#ESCAPE_NON_ASCII} instead
*/
@Deprecated
ESCAPE_NON_ASCII(false),
// // Datatype coercion features
/**
* Feature that forces all Java numbers to be written as Strings,
* even if the underlying data format has non-textual representation
* (which is the case for JSON as well as all binary formats).
* Default state is 'false', meaning that Java numbers are to
* be serialized using basic numeric serialization (as JSON
* numbers, integral or floating point, for example).
* If enabled, all such numeric values are instead written out as
* textual values (which for JSON means quoted in double-quotes).
*<p>
* One use case is to avoid problems with Javascript limitations:
* since Javascript standard specifies that all number handling
* should be done using 64-bit IEEE 754 floating point values,
* result being that some 64-bit integer values can not be
* accurately represent (as mantissa is only 51 bit wide).
*<p>
* Feature is disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NUMBERS_AS_STRINGS} instead
*/
@Deprecated
WRITE_NUMBERS_AS_STRINGS(false),
/**
* Feature that determines whether {@link java.math.BigDecimal} entries are
* serialized using {@link java.math.BigDecimal#toPlainString()} to prevent
* values to be written using scientific notation.
*<p>
* NOTE: only affects generators that serialize {@link java.math.BigDecimal}s
* using textual representation (textual formats but potentially some binary
* formats).
*<p>
* Feature is disabled by default, so default output mode is used; this generally
* depends on how {@link BigDecimal} has been created.
*
* @since 2.3
*/
WRITE_BIGDECIMAL_AS_PLAIN(false),
// // Schema/Validity support features
/**
* Feature that determines whether {@link JsonGenerator} will explicitly
* check that no duplicate JSON Object field names are written.
* If enabled, generator will check all names within context and report
* duplicates by throwing a {@link JsonGenerationException}; if disabled,
* no such checking will be done. Assumption in latter case is
* that caller takes care of not trying to write duplicate names.
*<p>
* Note that enabling this feature will incur performance overhead
* due to having to store and check additional information.
*<p>
* Feature is disabled by default.
*
* @since 2.3
*/
STRICT_DUPLICATE_DETECTION(false),
/**
* Feature that determines what to do if the underlying data format requires knowledge
* of all properties to output, and if no definition is found for a property that
* caller tries to write. If enabled, such properties will be quietly ignored;
* if disabled, a {@link JsonProcessingException} will be thrown to indicate the
* problem.
* Typically most textual data formats do NOT require schema information (although
* some do, such as CSV), whereas many binary data formats do require definitions
* (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not).
*<p>
* Note that support for this feature is implemented by individual data format
* module, if (and only if) it makes sense for the format in question. For JSON,
* for example, this feature has no effect as properties need not be pre-defined.
*<p>
* Feature is disabled by default, meaning that if the underlying data format
* requires knowledge of all properties to output, attempts to write an unknown
* property will result in a {@link JsonProcessingException}
*
* @since 2.5
*/
IGNORE_UNKNOWN(false),
;
private final boolean _defaultState;
private final int _mask;
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (Feature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
private Feature(boolean defaultState) {
_defaultState = defaultState;
_mask = (1 << ordinal());
}
public boolean enabledByDefault() { return _defaultState; }
/**
* @since 2.3
*/
public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public int getMask() { return _mask; }
}
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Object that handles pretty-printing (usually additional
* white space to make results more human-readable) during
* output. If null, no pretty-printing is done.
*/
protected PrettyPrinter _cfgPrettyPrinter;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
protected JsonGenerator() { }
/**
* Method that can be called to set or reset the object to
* use for writing Java objects as JsonContent
* (using method {@link #writeObject}).
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator setCodec(ObjectCodec oc);
/**
* Method for accessing the object used for writing Java
* object as JSON content
* (using method {@link #writeObject}).
*/
public abstract ObjectCodec getCodec();
/**
* Accessor for finding out version of the bundle that provided this generator instance.
*/
@Override
public abstract Version version();
/*
/**********************************************************
/* Public API, Feature configuration
/**********************************************************
*/
/**
* Method for enabling specified parser features:
* check {@link Feature} for list of available features.
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator enable(Feature f);
/**
* Method for disabling specified features
* (check {@link Feature} for list of features)
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator disable(Feature f);
/**
* Method for enabling or disabling specified feature:
* check {@link Feature} for list of available features.
*
* @return Generator itself (this), to allow chaining
*/
public final JsonGenerator configure(Feature f, boolean state) {
if (state) enable(f); else disable(f);
return this;
}
/**
* Method for checking whether given feature is enabled.
* Check {@link Feature} for list of available features.
*/
public abstract boolean isEnabled(Feature f);
/**
* @since 2.10
*/
public boolean isEnabled(StreamWriteFeature f) {
return isEnabled(f.mappedFeature());
}
/**
* Bulk access method for getting state of all standard (non-dataformat-specific)
* {@link JsonGenerator.Feature}s.
*
* @return Bit mask that defines current states of all standard {@link JsonGenerator.Feature}s.
*
* @since 2.3
*/
public abstract int getFeatureMask();
/**
* Bulk set method for (re)setting states of all standard {@link Feature}s
*
* @since 2.3
*
* @param values Bitmask that defines which {@link Feature}s are enabled
* and which disabled
*
* @return This parser object, to allow chaining of calls
*
* @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead -- remove from 2.9
*/
@Deprecated
public abstract JsonGenerator setFeatureMask(int values);
/**
* Bulk set method for (re)setting states of features specified by <code>mask</code>.
* Functionally equivalent to
*<code>
* int oldState = getFeatureMask();
* int newState = (oldState &amp; ~mask) | (values &amp; mask);
* setFeatureMask(newState);
*</code>
* but preferred as this lets caller more efficiently specify actual changes made.
*
* @param values Bit mask of set/clear state for features to change
* @param mask Bit mask of features to change
*
* @since 2.6
*/
public JsonGenerator overrideStdFeatures(int values, int mask) {
int oldState = getFeatureMask();
int newState = (oldState & ~mask) | (values & mask);
return setFeatureMask(newState);
}
/**
* Bulk access method for getting state of all {@link FormatFeature}s, format-specific
* on/off configuration settings.
*
* @return Bit mask that defines current states of all standard {@link FormatFeature}s.
*
* @since 2.6
*/
public int getFormatFeatures() {
return 0;
}
/**
* Bulk set method for (re)setting states of {@link FormatFeature}s,
* by specifying values (set / clear) along with a mask, to determine
* which features to change, if any.
*<p>
* Default implementation will simply throw an exception to indicate that
* the generator implementation does not support any {@link FormatFeature}s.
*
* @param values Bit mask of set/clear state for features to change
* @param mask Bit mask of features to change
*
* @since 2.6
*/
public JsonGenerator overrideFormatFeatures(int values, int mask) {
// 08-Oct-2018, tatu: For 2.10 we actually do get `JsonWriteFeature`s, although they
// are (for 2.x only, not for 3.x) mapper to legacy settings. So do not freak out:
// throw new IllegalArgumentException("No FormatFeatures defined for generator of type "+getClass().getName());
return this;
}
/*
/**********************************************************
/* Public API, Schema configuration
/**********************************************************
*/
/**
* Method to call to make this generator use specified schema.
* Method must be called before generating any content, right after instance
* has been created.
* Note that not all generators support schemas; and those that do usually only
* accept specific types of schemas: ones defined for data format this generator
* produces.
*<p>
* If generator does not support specified schema, {@link UnsupportedOperationException}
* is thrown.
*
* @param schema Schema to use
*
* @throws UnsupportedOperationException if generator does not support schema
*/
public void setSchema(FormatSchema schema) {
throw new UnsupportedOperationException(String.format(
"Generator of type %s does not support schema of type '%s'",
getClass().getName(), schema.getSchemaType()));
}
/**
* Method for accessing Schema that this parser uses, if any.
* Default implementation returns null.
*
* @since 2.1
*/
public FormatSchema getSchema() { return null; }
/*
/**********************************************************
/* Public API, other configuration
/**********************************************************
*/
/**
* Method for setting a custom pretty printer, which is usually
* used to add indentation for improved human readability.
* By default, generator does not do pretty printing.
*<p>
* To use the default pretty printer that comes with core
* Jackson distribution, call {@link #useDefaultPrettyPrinter}
* instead.
*
* @return Generator itself (this), to allow chaining
*/
public JsonGenerator setPrettyPrinter(PrettyPrinter pp) {
_cfgPrettyPrinter = pp;
return this;
}
/**
* Accessor for checking whether this generator has a configured
* {@link PrettyPrinter}; returns it if so, null if none configured.
*
* @since 2.1
*/
public PrettyPrinter getPrettyPrinter() {
return _cfgPrettyPrinter;
}
/**
* Convenience method for enabling pretty-printing using
* the default pretty printer
* ({@link com.fasterxml.jackson.core.util.DefaultPrettyPrinter}).
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator useDefaultPrettyPrinter();
/**
* Method that can be called to request that generator escapes
* all character codes above specified code point (if positive value);
* or, to not escape any characters except for ones that must be
* escaped for the data format (if -1).
* To force escaping of all non-ASCII characters, for example,
* this method would be called with value of 127.
*<p>
* Note that generators are NOT required to support setting of value
* higher than 127, because there are other ways to affect quoting
* (or lack thereof) of character codes between 0 and 127.
* Not all generators support concept of escaping, either; if so,
* calling this method will have no effect.
*<p>
* Default implementation does nothing; sub-classes need to redefine
* it according to rules of supported data format.
*
* @param charCode Either -1 to indicate that no additional escaping
* is to be done; or highest code point not to escape (meaning higher
* ones will be), if positive value.
*/
public JsonGenerator setHighestNonEscapedChar(int charCode) { return this; }
/**
* 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 getHighestEscapedChar() { return 0; }
/**
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*/
public CharacterEscapes getCharacterEscapes() { return null; }
/**
* Method for defining custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*<p>
* Default implementation does nothing and simply returns this instance.
*/
public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { return this; }
/**
* Method that allows overriding String used for separating root-level
* JSON values (default is single space character)
*<p>
* Default implementation throws {@link UnsupportedOperationException}.
*
* @param sep Separator to use, if any; null means that no separator is
* automatically added
*
* @since 2.1
*/
public JsonGenerator setRootValueSeparator(SerializableString sep) {
throw new UnsupportedOperationException();
}
/*
/**********************************************************
/* Public API, output state access
/**********************************************************
*/
/**
* Method that can be used to get access to object that is used
* as target for generated output; this is usually either
* {@link OutputStream} or {@link 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.
*/
public Object getOutputTarget() {
return null;
}
/**
* 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,
* `byte` for {@link java.io.OutputStream}, `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: non-JSON implementations will be retrofitted for 2.6 and beyond;
* please report if you see -1 (missing override)
*
* @return Amount of content buffered in internal units, if amount known and
* accessible; -1 if not accessible.
*
* @since 2.6
*/
public int getOutputBuffered() {
return -1;
}
/**
* Helper method, usually equivalent to:
*<code>
* getOutputContext().getCurrentValue();
*</code>
*<p>
* Note that "current value" is NOT populated (or used) by Streaming parser;
* 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.
*
* @since 2.5
*/
public Object getCurrentValue() {
JsonStreamContext ctxt = getOutputContext();
return (ctxt == null) ? null : ctxt.getCurrentValue();
}
/**
* Helper method, usually equivalent to:
*<code>
* getOutputContext().setCurrentValue(v);
*</code>
*
* @since 2.5
*/
public void setCurrentValue(Object v) {
JsonStreamContext ctxt = getOutputContext();
if (ctxt != null) {
ctxt.setCurrentValue(v);
}
}
/*
/**********************************************************
/* Public API, capability introspection methods
/**********************************************************
*/
/**
* Method that can be used to verify that given schema can be used with
* this generator (using {@link #setSchema}).
*
* @param schema Schema to check
*
* @return True if this generator can use given schema; false if not
*/
public boolean canUseSchema(FormatSchema schema) { return false; }
/**
* 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 can not use native object ids.
*
* @since 2.3
*/
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 can not use native type ids.
*
* @since 2.3
*/
public boolean canWriteTypeId() { return false; }
/**
* Introspection method that may be called to see if the underlying
* data format supports "native" binary data; that is, an efficient
* output of binary content without encoding.
*<p>
* Default implementation returns false; overridden by data formats
* that do support native binary content.
*
* @since 2.3
*/
public boolean canWriteBinaryNatively() { return false; }
/**
* Introspection method to call to check whether it is ok to omit
* writing of Object fields or not. Most formats do allow omission,
* but certain positional formats (such as CSV) require output of
* placeholders, even if no real values are to be emitted.
*
* @since 2.3
*/
public boolean canOmitFields() { return true; }
/**
* Introspection method to call to check whether it is possible
* to write numbers using {@link #writeNumber(java.lang.String)}
* using possible custom format, or not. Typically textual formats
* allow this (and JSON specifically does), whereas binary formats
* do not allow this (except by writing them as Strings).
* Usual reason for calling this method is to check whether custom
* formatting of numbers may be applied by higher-level code (databinding)
* or not.
*
* @since 2.8
*/
public boolean canWriteFormattedNumbers() { return false; }
/*
/**********************************************************
/* 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 field name is expected.
*/
public abstract void writeStartArray() throws IOException;
/**
* Method for writing start marker of an Array value, similar
* to {@link #writeStartArray()}, but also specifying how many
* elements will be written for the array before calling
* {@link #writeEndArray()}.
*<p>
* Default implementation simply calls {@link #writeStartArray()}.
*
* @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.
*
* @since 2.4
*
* @deprecated Since 2.12 Use {@link #writeStartArray(Object, int)} instead
*/
@Deprecated
public void writeStartArray(int size) throws IOException {
writeStartArray();
}
/**
* @since 2.10
*/
public void writeStartArray(Object forValue) throws IOException {
writeStartArray();
setCurrentValue(forValue);
}
/**
* @since 2.10
*/
public void writeStartArray(Object forValue, int size) throws IOException {
writeStartArray(size);
setCurrentValue(forValue);
}
/**
* 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.
*/
public abstract void writeEndArray() throws IOException;
/**
* 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 field name is expected.
*/
public abstract void writeStartObject() throws IOException;
/**
* 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 field name is expected.
*
* @since 2.8
*/
public void writeStartObject(Object forValue) throws IOException
{
writeStartObject();
setCurrentValue(forValue);
}
/**
* 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 field name is expected.
*
* @since 2.10
*/
public void writeStartObject(Object forValue, int size) throws IOException
{
writeStartObject();
setCurrentValue(forValue);
}
/**
* 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).
*/
public abstract void writeEndObject() throws IOException;
/**
* Method for writing a field 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>
* Field names can only be written in Object context (check out
* JSON specification for details), when field name is expected
* (field names alternate with values).
*/
public abstract void writeFieldName(String name) throws IOException;
/**
* Method similar to {@link #writeFieldName(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.
*/
public abstract void writeFieldName(SerializableString name) throws IOException;
/**
* Alternative to {@link #writeFieldName(String)} that may be used
* in cases where property key is of numeric type; either where
* underlying format supports such notion (some binary formats do,
* unlike JSON), or for convenient conversion into String presentation.
* Default implementation will simply convert id into <code>String</code>
* and call {@link #writeFieldName(String)}.
*
* @since 2.8
*/
public void writeFieldId(long id) throws IOException {
writeFieldName(Long.toString(id));
}
/*
/**********************************************************
/* Public API, write methods, scalar arrays (2.8)
/**********************************************************
*/
/**
* 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})
*
* @since 2.8
*
* @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`
*/
public void writeArray(int[] array, int offset, int length) throws IOException
{
if (array == null) {
throw new IllegalArgumentException("null array");
}
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
}
/**
* 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})
*
* @since 2.8
*
* @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`
*/
public void writeArray(long[] array, int offset, int length) throws IOException
{
if (array == null) {
throw new IllegalArgumentException("null array");
}
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
}
/**
* 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})
*
* @since 2.8
*
* @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`
*/
public void writeArray(double[] array, int offset, int length) throws IOException
{
if (array == null) {
throw new IllegalArgumentException("null array");
}
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
}
/**
* 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})
*
* @since 2.11
*
* @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`
*/
public void writeArray(String[] array, int offset, int length) throws IOException
{
if (array == null) {
throw new IllegalArgumentException("null array");
}
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeString(array[i]);
}
writeEndArray();
}
/*
/**********************************************************
/* Public API, write methods, text/String values
/**********************************************************
*/
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field 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.
*/
public abstract void writeString(String text) throws IOException;
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field 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 the reader is null, then write a null.
* If len is &lt; 0, then write all contents of the reader.
* Otherwise, write only len characters.
*
* @since 2.9
*/
public void writeString(Reader reader, int len) throws IOException {
// Let's implement this as "unsupported" to make it easier to add new parser impls
_reportUnsupportedOperation();
}
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field 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.
*/
public abstract void writeString(char[] text, int offset, int len) throws IOException;
/**
* 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.
*/
public abstract void writeString(SerializableString text) throws IOException;
/**
* 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.
*/
public abstract void writeRawUTF8String(byte[] text, int offset, int length)
throws IOException;
/**
* 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.
*/
public abstract void writeUTF8String(byte[] text, int offset, int length)
throws IOException;
/*
/**********************************************************
/* Public API, write methods, binary/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}.
*/
public abstract void writeRaw(String text) throws IOException;
/**
* 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}.
*/
public abstract void writeRaw(String text, int offset, int len) throws IOException;
/**
* 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}.
*/
public abstract void writeRaw(char[] text, int offset, int len) throws IOException;
/**
* 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}.
*/
public abstract void writeRaw(char c) throws IOException;
/**
* 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).
*
* @since 2.1
*/
// public abstract void writeRaw(SerializableString raw) throws IOException;
public void writeRaw(SerializableString raw) throws IOException {
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.
*/
public abstract void writeRawValue(String text) throws IOException;
public abstract void writeRawValue(String text, int offset, int len) throws IOException;
public abstract void writeRawValue(char[] text, int offset, int len) throws IOException;
/**
* 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)}.
*
* @since 2.5
*/
public void writeRawValue(SerializableString raw) throws IOException {
writeRawValue(raw.getValue());
}
/**
* 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 can not 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.
*/
public abstract void writeBinary(Base64Variant bv,
byte[] data, int offset, int len) throws IOException;
/**
* Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}).
*/
public void writeBinary(byte[] data, int offset, int len) throws IOException {
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.
*/
public void writeBinary(byte[] data) throws IOException {
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
*/
public int writeBinary(InputStream data, int dataLength)
throws IOException {
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
*
* @since 2.1
*/
public abstract int writeBinary(Base64Variant bv,
InputStream data, int dataLength) throws IOException;
/*
/**********************************************************
/* 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 field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @since 2.2
*/
public void writeNumber(short v) throws IOException { writeNumber((int) v); }
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(int v) throws IOException;
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(long v) throws IOException;
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(BigInteger v) throws IOException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(double v) throws IOException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(float v) throws IOException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(BigDecimal v) throws IOException;
/**
* Write method that can be used for custom numeric types that can
* not be (easily?) converted to "standard" Java number types.
* Because numbers are not surrounded by double quotes, regular
* {@link #writeString} method can not be used; nor
* {@link #writeRaw} because that does not properly handle
* value separators needed in Array or Object contexts.
*<p>
* Note: because of lack of type safety, some generator
* implementations may not be able to implement this
* method. For example, if a binary JSON format is used,
* it may require type information for encoding; similarly
* for generator-wrappers around Java objects or JSON nodes.
* If implementation does not implement this method,
* it needs to throw {@link UnsupportedOperationException}.
*
* @throws UnsupportedOperationException If underlying data format does not
* support numbers serialized textually AND if generator is not allowed
* to just output a String instead (Schema-based formats may require actual
* number, for example)
*/
public abstract void writeNumber(String encodedValue) throws IOException;
/**
* Overloaded version of {@link #writeNumber(String)} with same semantics
* but possibly more efficient operation.
*
* @since 2.11
*/
public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException {
writeNumber(new String(encodedValueBuffer, offset, length));
}
/*
/**********************************************************
/* 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 field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeBoolean(boolean state) throws IOException;
/**
* Method for outputting literal JSON null value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNull() throws IOException;
/**
* Method that can be called on backends that support passing opaque datatypes of
* non-JSON formats
*
* @since 2.8
*/
public void writeEmbeddedObject(Object object) throws IOException {
// 01-Sep-2016, tatu: As per [core#318], handle small number of cases
if (object == null) {
writeNull();
return;
}
if (object instanceof byte[]) {
writeBinary((byte[]) object);
return;
}
throw new JsonGenerationException("No native support for writing embedded objects of type "
+object.getClass().getName(),
this);
}
/*
/**********************************************************
/* 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.
* If output is not allowed by the data format in this position,
* a {@link JsonGenerationException} will be thrown.
*
* @since 2.3
*/
public void writeObjectId(Object id) throws IOException {
throw new JsonGenerationException("No native support for writing Object Ids", this);
}
/**
* 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 JsonGenerationException} will be thrown.
*/
public void writeObjectRef(Object id) throws IOException {
throw new JsonGenerationException("No native support for writing Object Ids", this);
}
/**
* 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 JsonGenerationException} will be thrown.
*
* @since 2.3
*/
public void writeTypeId(Object id) throws IOException {
throw new JsonGenerationException("No native support for writing Type Ids", this);
}
/*
* 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.
*
* @since 2.9
*/
public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException
{
Object id = typeIdDef.id;
final JsonToken valueShape = typeIdDef.valueShape;
if (canWriteTypeId()) {
typeIdDef.wrapperWritten = false;
// just rely on native type output method (sub-classes likely to override)
writeTypeId(id);
} else {
// No native type id; write wrappers
// Normally we only support String type ids (non-String reserved for native type ids)
String idStr = (id instanceof String) ? (String) id : String.valueOf(id);
typeIdDef.wrapperWritten = true;
Inclusion incl = typeIdDef.include;
// first: can not output "as property" if value not Object; if so, must do "as array"
if ((valueShape != JsonToken.START_OBJECT)
&& incl.requiresObjectContext()) {
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
}
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);
writeStringField(typeIdDef.asProperty, idStr);
return typeIdDef;
case WRAPPER_OBJECT:
// NOTE: this is wrapper, not directly related to value to output, so don't pass
writeStartObject();
writeFieldName(idStr);
break;
case WRAPPER_ARRAY:
default: // should never occur but translate as "as-array"
writeStartArray(); // wrapper, not actual array object to write
writeString(idStr);
}
}
// and finally possible start marker for value itself:
if (valueShape == JsonToken.START_OBJECT) {
writeStartObject(typeIdDef.forValue);
} else if (valueShape == JsonToken.START_ARRAY) {
// should we now set the current object?
writeStartArray();
}
return typeIdDef;
}
/*
* @since 2.9
*/
public WritableTypeId writeTypeSuffix(WritableTypeId typeIdDef) throws IOException
{
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);
writeStringField(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 Json.
* Exactly how the object gets written depends on object
* in question (ad on codec, its configuration); for most
* beans it will result in JSON Object, but for others JSON
* Array, or String or numeric value (and for nulls, JSON
* null literal.
* <b>NOTE</b>: generator must have its <b>object codec</b>
* set to non-null value; for generators created by a mapping
* factory this is the case, for others not.
*/
public abstract void writeObject(Object pojo) throws IOException;
/**
* Method for writing given JSON tree (expressed as a tree
* where given JsonNode is the root) using this generator.
* This will generally just call
* {@link #writeObject} with given node, but is added
* for convenience and to make code more explicit in cases
* where it deals specifically with trees.
*/
public abstract void writeTree(TreeNode rootNode) throws IOException;
/*
/**********************************************************
/* Public API, convenience field write methods
/**********************************************************
*/
// 04-Oct-2019, tatu: Reminder: these could be defined final to
// remember NOT to override in delegating sub-classes -- but
// not final in 2.x to reduce compatibility issues
/**
* Convenience method for outputting a field entry ("member")
* that contains specified data in base64-encoded form.
* Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeBinary(value);
*</pre>
*/
public void writeBinaryField(String fieldName, byte[] data) throws IOException {
writeFieldName(fieldName);
writeBinary(data);
}
/**
* Convenience method for outputting a field entry ("member")
* that has a boolean value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeBoolean(value);
*</pre>
*/
public void writeBooleanField(String fieldName, boolean value) throws IOException {
writeFieldName(fieldName);
writeBoolean(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has JSON literal value null. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNull();
*</pre>
*/
public void writeNullField(String fieldName) throws IOException {
writeFieldName(fieldName);
writeNull();
}
/**
* Convenience method for outputting a field entry ("member")
* that has a String value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeString(value);
*</pre>
*<p>
* Note: many performance-sensitive implementations override this method
*/
public void writeStringField(String fieldName, String value) throws IOException {
writeFieldName(fieldName);
writeString(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNumber(value);
*</pre>
*
* @since 2.11
*/
public void writeNumberField(String fieldName, short value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNumber(value);
*</pre>
*/
public void writeNumberField(String fieldName, int value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNumber(value);
*</pre>
*/
public void writeNumberField(String fieldName, long value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNumber(value);
*</pre>
*
* @since 2.11
*/
public void writeNumberField(String fieldName, BigInteger value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNumber(value);
*</pre>
*/
public void writeNumberField(String fieldName, float value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNumber(value);
*</pre>
*/
public void writeNumberField(String fieldName, double value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value.
* Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeNumber(value);
*</pre>
*/
public void writeNumberField(String fieldName, BigDecimal value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* (that will contain a JSON Array value), and the START_ARRAY marker.
* Equivalent to:
*<pre>
* writeFieldName(fieldName);
* 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.
*/
public void writeArrayFieldStart(String fieldName) throws IOException {
writeFieldName(fieldName);
writeStartArray();
}
/**
* Convenience method for outputting a field entry ("member")
* (that will contain an Object value), and the START_OBJECT marker.
* Equivalent to:
*<pre>
* writeFieldName(fieldName);
* 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.
*/
public void writeObjectFieldStart(String fieldName) throws IOException {
writeFieldName(fieldName);
writeStartObject();
}
/**
* Convenience method for outputting a field entry ("member")
* that has contents of specific Java object as its value.
* Equivalent to:
*<pre>
* writeFieldName(fieldName);
* writeObject(pojo);
*</pre>
*/
public void writeObjectField(String fieldName, Object pojo) throws IOException {
writeFieldName(fieldName);
writeObject(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 #canOmitFields()}.
*<p>
* Default implementation does nothing.
*
* @since 2.3
*/
public void writeOmittedField(String fieldName) throws IOException { }
/*
/**********************************************************
/* Public API, copy-through methods
/**********************************************************
*/
/**
* 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)
*/
public void copyCurrentEvent(JsonParser p) throws IOException
{
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_FIELD_NAME:
writeFieldName(p.getCurrentName());
break;
case ID_STRING:
if (p.hasTextCharacters()) {
writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength());
} else {
writeString(p.getText());
}
break;
case ID_NUMBER_INT:
{
NumberType n = p.getNumberType();
if (n == NumberType.INT) {
writeNumber(p.getIntValue());
} else if (n == NumberType.BIG_INTEGER) {
writeNumber(p.getBigIntegerValue());
} else {
writeNumber(p.getLongValue());
}
break;
}
case ID_NUMBER_FLOAT:
{
NumberType n = p.getNumberType();
if (n == NumberType.BIG_DECIMAL) {
writeNumber(p.getDecimalValue());
} else if (n == NumberType.FLOAT) {
writeNumber(p.getFloatValue());
} else {
writeNumber(p.getDoubleValue());
}
break;
}
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writeObject(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#FIELD_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>field entry</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.
*/
public void copyCurrentStructure(JsonParser p) throws IOException
{
JsonToken t = p.currentToken();
// Let's handle field-name separately first
int id = (t == null) ? ID_NOT_AVAILABLE : t.id();
if (id == ID_FIELD_NAME) {
writeFieldName(p.getCurrentName());
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);
}
}
/**
* @since 2.10
*/
protected void _copyCurrentContents(JsonParser p) throws IOException
{
int depth = 1;
JsonToken t;
// Mostly copied from `copyCurrentEvent()`, but with added nesting counts
while ((t = p.nextToken()) != null) {
switch (t.id()) {
case ID_FIELD_NAME:
writeFieldName(p.getCurrentName());
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:
if (p.hasTextCharacters()) {
writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength());
} else {
writeString(p.getText());
}
break;
case ID_NUMBER_INT:
{
NumberType n = p.getNumberType();
if (n == NumberType.INT) {
writeNumber(p.getIntValue());
} else if (n == NumberType.BIG_INTEGER) {
writeNumber(p.getBigIntegerValue());
} else {
writeNumber(p.getLongValue());
}
break;
}
case ID_NUMBER_FLOAT:
{
NumberType n = p.getNumberType();
if (n == NumberType.BIG_DECIMAL) {
writeNumber(p.getDecimalValue());
} else if (n == NumberType.FLOAT) {
writeNumber(p.getFloatValue());
} else {
writeNumber(p.getDoubleValue());
}
break;
}
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writeObject(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
}
/*
/**********************************************************
/* Public API, context access
/**********************************************************
*/
/**
* @return Context object that can give information about logical
* position within generated json content.
*/
public abstract JsonStreamContext getOutputContext();
/*
/**********************************************************
/* 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() throws IOException;
/**
* Method that can be called to determine whether this generator
* is closed or not. If it is closed, no more output can be done.
*/
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 Feature#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() throws IOException;
/*
/**********************************************************
/* Helper methods for sub-classes
/**********************************************************
*/
/**
* Helper method used for constructing and throwing
* {@link JsonGenerationException} with given base message.
*<p>
* Note that sub-classes may override this method to add more detail
* or use a {@link JsonGenerationException} sub-class.
*/
protected void _reportError(String msg) throws JsonGenerationException {
throw new JsonGenerationException(msg, this);
}
protected final void _throwInternal() { VersionUtil.throwInternal(); }
protected void _reportUnsupportedOperation() {
throw new UnsupportedOperationException("Operation not supported by generator of type "+getClass().getName());
}
/**
* @since 2.8
*/
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));
}
}
/**
* Helper method to try to call appropriate write method for given
* untyped Object. At this point, no structural conversions should be done,
* only simple basic types are to be coerced as necessary.
*
* @param value Non-null value to write
*/
protected void _writeSimpleObject(Object value) throws IOException
{
// 31-Dec-2009, tatu: Actually, we could just handle some basic
// types even without codec. This can improve interoperability,
// and specifically help with TokenBuffer.
if (value == null) {
writeNull();
return;
}
if (value instanceof String) {
writeString((String) value);
return;
}
if (value instanceof Number) {
Number n = (Number) value;
if (n instanceof Integer) {
writeNumber(n.intValue());
return;
} else if (n instanceof Long) {
writeNumber(n.longValue());
return;
} else if (n instanceof Double) {
writeNumber(n.doubleValue());
return;
} else if (n instanceof Float) {
writeNumber(n.floatValue());
return;
} else if (n instanceof Short) {
writeNumber(n.shortValue());
return;
} else if (n instanceof Byte) {
writeNumber(n.byteValue());
return;
} else if (n instanceof BigInteger) {
writeNumber((BigInteger) n);
return;
} else if (n instanceof BigDecimal) {
writeNumber((BigDecimal) n);
return;
// then Atomic types
} else if (n instanceof AtomicInteger) {
writeNumber(((AtomicInteger) n).get());
return;
} else if (n instanceof AtomicLong) {
writeNumber(((AtomicLong) n).get());
return;
}
} else if (value instanceof byte[]) {
writeBinary((byte[]) value);
return;
} else if (value instanceof Boolean) {
writeBoolean((Boolean) value);
return;
} else if (value instanceof AtomicBoolean) {
writeBoolean(((AtomicBoolean) value).get());
return;
}
throw new IllegalStateException("No ObjectCodec defined for the generator, can only serialize simple wrapper types (type passed "
+value.getClass().getName()+")");
}
}