InternalNodeSerializer.java
package tools.jackson.databind.node;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.*;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.jsontype.TypeSerializer;
/**
* Helper class used to support non-recursive serialization for use
* by {@link BaseJsonNode#toString} (and related) method.
*/
class InternalNodeSerializer
{
private final static JsonMapper MAPPER = JsonMapper.shared();
private final static ObjectWriter COMPACT_WRITER = MAPPER.writer();
private final static ObjectWriter PRETTY_WRITER = MAPPER.writerWithDefaultPrettyPrinter();
private static JacksonSerializable _wrapper(BaseJsonNode root) {
return new WrapperForSerializer(root);
}
public static String toString(BaseJsonNode root) {
return COMPACT_WRITER.writeValueAsString(_wrapper(root));
}
public static String toPrettyString(BaseJsonNode root) {
return PRETTY_WRITER.writeValueAsString(_wrapper(root));
}
/**
* Intermediate serializer we need to implement non-recursive serialization of
* {@link BaseJsonNode}
*/
protected static class WrapperForSerializer
extends JacksonSerializable.Base
{
protected final BaseJsonNode _root;
// Non-final as passed when `serialize()` is called
protected SerializationContext _context;
public WrapperForSerializer(BaseJsonNode root) {
_root = root;
}
@Override
public void serialize(JsonGenerator g, SerializationContext ctxt) throws JacksonException
{
_context = ctxt;
_serializeNonRecursive(g, _root);
}
@Override
public void serializeWithType(JsonGenerator gen, SerializationContext serializers,
TypeSerializer typeSer) throws JacksonException
{
// Should not really be called given usage, so
serialize(gen, serializers);
}
protected void _serializeNonRecursive(JsonGenerator g, JsonNode node)
throws JacksonException
{
if (node instanceof ObjectNode) {
g.writeStartObject(this, node.size());
_serializeNonRecursive(g, new IteratorStack(), node.properties().iterator());
} else if (node instanceof ArrayNode) {
g.writeStartArray(this, node.size());
_serializeNonRecursive(g, new IteratorStack(), node.iterator());
} else {
node.serialize(g, _context);
}
}
protected void _serializeNonRecursive(JsonGenerator g, IteratorStack stack,
final Iterator<?> rootIterator)
throws JacksonException
{
Iterator<?> currIt = rootIterator;
while (true) {
// First: any more elements from the current iterator?
while (currIt.hasNext()) {
JsonNode value;
// Otherwise we do have another Map or Array element to handle
Object elem = currIt.next();
if (elem instanceof Map.Entry<?,?>) {
@SuppressWarnings("unchecked")
Map.Entry<String, JsonNode> en = (Map.Entry<String, JsonNode>) elem;
g.writeName(en.getKey());
value = en.getValue();
} else {
value = (JsonNode) elem;
}
if (value instanceof ObjectNode) {
stack.push(currIt);
currIt = value.properties().iterator();
g.writeStartObject(value, value.size());
} else if (value instanceof ArrayNode) {
stack.push(currIt);
currIt = value.iterator();
g.writeStartArray(value, value.size());
} else if (value instanceof POJONode) {
// [databind#3262] Problematic case, try to handle
try {
value.serialize(g, _context);
} catch (JacksonException e) {
g.writeString(String.format("[ERROR: (%s) %s]",
e.getClass().getName(), e.getMessage()));
}
} else {
value.serialize(g, _context);
}
}
if (g.streamWriteContext().inArray()) {
g.writeEndArray();
} else {
g.writeEndObject();
}
currIt = stack.popOrNull();
if (currIt == null) {
return;
}
}
}
}
/**
* Optimized variant similar in functionality to (a subset of)
* {@link java.util.ArrayDeque}; used to hold enclosing Array/Object
* nodes during recursion-as-iteration.
*/
final static class IteratorStack
{
private Iterator<?>[] _stack;
private int _top, _end;
public IteratorStack() { }
public void push(Iterator<?> it)
{
if (_top < _end) {
_stack[_top++] = it; // lgtm [java/dereferenced-value-may-be-null]
return;
}
if (_stack == null) {
_end = 10;
_stack = new Iterator<?>[_end];
} else {
// grow by 50%, for most part
_end += Math.min(4000, Math.max(20, _end>>1));
_stack = Arrays.copyOf(_stack, _end);
}
_stack[_top++] = it;
}
public Iterator<?> popOrNull() {
if (_top == 0) {
return null;
}
// note: could clean up stack but due to usage pattern, should not make
// much difference since the whole stack is discarded after serialization done
return _stack[--_top];
}
}
}