All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.jackson.databind.node.InternalNodeMapper Maven / Gradle / Ivy

There is a newer version: 2.17.1
Show newest version
package com.fasterxml.jackson.databind.node;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

import com.fasterxml.jackson.core.JsonGenerator;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;

/**
 * Helper class used to implement {@code toString()} method for
 * {@link BaseJsonNode}, by embedding a private instance of
 * {@link JsonMapper}, only to be used for node serialization.
 *
 * @since 2.10 (but not to be included in 3.0)
 */
final class InternalNodeMapper {
    private final static JsonMapper JSON_MAPPER = new JsonMapper();

    private final static ObjectWriter STD_WRITER = JSON_MAPPER.writer();
    private final static ObjectWriter PRETTY_WRITER = JSON_MAPPER.writer()
            .withDefaultPrettyPrinter();

    private final static ObjectReader NODE_READER = JSON_MAPPER.readerFor(JsonNode.class);

    // // // Methods for `JsonNode.toString()` and `JsonNode.toPrettyString()`

    public static String nodeToString(BaseJsonNode n) {
        try {
            return STD_WRITER.writeValueAsString(_wrapper(n));
        } catch (IOException e) { // should never occur
            throw new RuntimeException(e);
        }
    }

    public static String nodeToPrettyString(BaseJsonNode n) {
        try {
            return PRETTY_WRITER.writeValueAsString(_wrapper(n));
        } catch (IOException e) { // should never occur
            throw new RuntimeException(e);
        }
    }

    // // // Methods for JDK serialization support of JsonNodes

    public static byte[] valueToBytes(Object value) throws IOException {
        return JSON_MAPPER.writeValueAsBytes(value);
    }

    public static JsonNode bytesToNode(byte[] json) throws IOException {
        return NODE_READER.readValue(json);
    }

    private static JsonSerializable _wrapper(BaseJsonNode root) {
        return new WrapperForSerializer(root);
    }

    /**
     * Intermediate serializer we need to implement non-recursive serialization of
     * {@link BaseJsonNode}.
     *

* NOTE: not designed as thread-safe; instances must NOT be shared or reused. * * @since 2.14 */ protected static class WrapperForSerializer extends JsonSerializable.Base { protected final BaseJsonNode _root; // Non-final as passed when `serialize()` is called protected SerializerProvider _context; public WrapperForSerializer(BaseJsonNode root) { _root = root; } @Override public void serialize(JsonGenerator g, SerializerProvider ctxt) throws IOException { _context = ctxt; _serializeNonRecursive(g, _root); } @Override public void serializeWithType(JsonGenerator g, SerializerProvider ctxt, TypeSerializer typeSer) throws IOException { // Should not really be called given usage, so serialize(g, ctxt); } protected void _serializeNonRecursive(JsonGenerator g, JsonNode node) throws IOException { if (node instanceof ObjectNode) { g.writeStartObject(this, node.size()); _serializeNonRecursive(g, new IteratorStack(), node.fields()); } else if (node instanceof ArrayNode) { g.writeStartArray(this, node.size()); _serializeNonRecursive(g, new IteratorStack(), node.elements()); } else { node.serialize(g, _context); } } protected void _serializeNonRecursive(JsonGenerator g, IteratorStack stack, final Iterator rootIterator) throws IOException { 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 en = (Map.Entry) elem; g.writeFieldName(en.getKey()); value = en.getValue(); } else { value = (JsonNode) elem; } if (value instanceof ObjectNode) { stack.push(currIt); currIt = value.fields(); g.writeStartObject(value, value.size()); } else if (value instanceof ArrayNode) { stack.push(currIt); currIt = value.elements(); g.writeStartArray(value, value.size()); } else if (value instanceof POJONode) { // [databind#3262] Problematic case, try to handle try { value.serialize(g, _context); } catch (IOException | RuntimeException e) { g.writeString(String.format("[ERROR: (%s) %s]", e.getClass().getName(), e.getMessage())); } } else { value.serialize(g, _context); } } if (g.getOutputContext().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]; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy