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

co.elastic.clients.json.jackson.JacksonJsonpParser Maven / Gradle / Ivy

/*
 * Licensed to Elasticsearch B.V. under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch B.V. licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package co.elastic.clients.json.jackson;

import co.elastic.clients.json.BufferingJsonParser;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.LookAheadJsonParser;
import co.elastic.clients.json.UnexpectedJsonEventException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.util.JsonParserSequence;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import jakarta.json.stream.JsonLocation;
import jakarta.json.stream.JsonParser;
import jakarta.json.stream.JsonParsingException;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.AbstractMap;
import java.util.EnumMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Stream;

/**
 * A JSONP parser implementation on top of Jackson.
 * 

* Warning: this implementation isn't fully compliant with the JSONP specification: calling {@link #hasNext()} * moves forward the underlying Jackson parser as Jackson doesn't provide an equivalent method. This means no value * getter method (e.g. {@link #getInt()} or {@link #getString()} should be called until the next call to {@link #next()}. * Such calls will throw an {@code IllegalStateException}. */ public class JacksonJsonpParser implements LookAheadJsonParser, BufferingJsonParser { private final com.fasterxml.jackson.core.JsonParser parser; private final JacksonJsonpMapper mapper; private boolean hasNextWasCalled = false; private static final EnumMap tokenToEvent; static { tokenToEvent = new EnumMap<>(JsonToken.class); tokenToEvent.put(JsonToken.END_ARRAY, Event.END_ARRAY); tokenToEvent.put(JsonToken.END_OBJECT, Event.END_OBJECT); tokenToEvent.put(JsonToken.FIELD_NAME, Event.KEY_NAME); tokenToEvent.put(JsonToken.START_ARRAY, Event.START_ARRAY); tokenToEvent.put(JsonToken.START_OBJECT, Event.START_OBJECT); tokenToEvent.put(JsonToken.VALUE_FALSE, Event.VALUE_FALSE); tokenToEvent.put(JsonToken.VALUE_NULL, Event.VALUE_NULL); tokenToEvent.put(JsonToken.VALUE_NUMBER_FLOAT, Event.VALUE_NUMBER); tokenToEvent.put(JsonToken.VALUE_NUMBER_INT, Event.VALUE_NUMBER); tokenToEvent.put(JsonToken.VALUE_STRING, Event.VALUE_STRING); tokenToEvent.put(JsonToken.VALUE_TRUE, Event.VALUE_TRUE); // No equivalent for // - VALUE_EMBEDDED_OBJECT // - NOT_AVAILABLE } public JacksonJsonpParser(com.fasterxml.jackson.core.JsonParser parser, JacksonJsonpMapper mapper) { this.parser = parser; this.mapper = mapper; } /** * Returns the underlying Jackson parser. */ public com.fasterxml.jackson.core.JsonParser jacksonParser() { return this.parser; } private JsonParsingException convertException(IOException ioe) { return new JsonParsingException("Jackson exception: " + ioe.getMessage(), ioe, getLocation()); } private JsonToken fetchNextToken() { try { return parser.nextToken(); } catch(IOException e) { throw convertException(e); } } private void ensureTokenIsCurrent() { if (hasNextWasCalled) { throw new IllegalStateException("Cannot get event data as parser as already been moved to the next event"); } } @Override public boolean hasNext() { if (hasNextWasCalled) { return parser.currentToken() != null; } else { hasNextWasCalled = true; return fetchNextToken() != null; } } @Override public Event next() { JsonToken token; if (hasNextWasCalled) { token = parser.getCurrentToken(); hasNextWasCalled = false; } else { token = fetchNextToken(); } if (token == null) { throw new NoSuchElementException(); } Event result = tokenToEvent.get(token); if (result == null) { throw new JsonParsingException("Unsupported Jackson event type '"+ token + "'", getLocation()); } return result; } @Override public String getString() { ensureTokenIsCurrent(); try { return parser.getValueAsString(); } catch (IOException e) { throw convertException(e); } } @Override public boolean isIntegralNumber() { ensureTokenIsCurrent(); return parser.isExpectedNumberIntToken(); } @Override public int getInt() { ensureTokenIsCurrent(); try { return parser.getIntValue(); } catch (IOException e) { throw convertException(e); } } @Override public long getLong() { ensureTokenIsCurrent(); try { return parser.getLongValue(); } catch (IOException e) { throw convertException(e); } } @Override public BigDecimal getBigDecimal() { ensureTokenIsCurrent(); try { return parser.getDecimalValue(); } catch (IOException e) { throw convertException(e); } } @Override public JsonLocation getLocation() { return new JacksonJsonpLocation(parser.getCurrentLocation()); } @Override public void close() { try { parser.close(); } catch (IOException e) { throw convertException(e); } } private JsonValueParser valueParser; @Override public JsonObject getObject() { ensureTokenIsCurrent(); if (parser.currentToken() != JsonToken.START_OBJECT) { throw new IllegalStateException("Unexpected event '" + parser.currentToken() + "' at " + parser.getTokenLocation()); } if (valueParser == null) { valueParser = new JsonValueParser(); } try { return valueParser.parseObject(parser); } catch (IOException e) { throw convertException(e); } } @Override public JsonArray getArray() { ensureTokenIsCurrent(); if (valueParser == null) { valueParser = new JsonValueParser(); } if (parser.currentToken() != JsonToken.START_ARRAY) { throw new IllegalStateException("Unexpected event '" + parser.currentToken() + "' at " + parser.getTokenLocation()); } try { return valueParser.parseArray(parser); } catch (IOException e) { throw convertException(e); } } @Override public JsonValue getValue() { ensureTokenIsCurrent(); if (valueParser == null) { valueParser = new JsonValueParser(); } try { return valueParser.parseValue(parser); } catch (IOException e) { throw convertException(e); } } @Override public void skipObject() { ensureTokenIsCurrent(); if (parser.currentToken() != JsonToken.START_OBJECT) { return; } try { int depth = 1; JsonToken token; do { token = parser.nextToken(); switch (token) { case START_OBJECT: depth++; break; case END_OBJECT: depth--; break; } } while(!(token == JsonToken.END_OBJECT && depth == 0)); } catch (IOException e) { throw convertException(e); } } @Override public void skipArray() { ensureTokenIsCurrent(); if (parser.currentToken() != JsonToken.START_ARRAY) { return; } try { int depth = 1; JsonToken token; do { token = parser.nextToken(); switch (token) { case START_ARRAY: depth++; break; case END_ARRAY: depth--; break; } } while(!(token == JsonToken.END_ARRAY && depth == 0)); } catch (IOException e) { throw convertException(e); } } @Override public Stream> getObjectStream() { return getObject().entrySet().stream(); } @Override public Stream getArrayStream() { return getArray().stream(); } /** * Not implemented. */ @Override public Stream getValueStream() { return LookAheadJsonParser.super.getValueStream(); } //----- Look ahead methods public Map.Entry lookAheadFieldValue(String name, String defaultValue) { TokenBuffer tb = new TokenBuffer(parser, null); try { // The resulting parser must contain the full object, including START_EVENT tb.copyCurrentEvent(parser); while (parser.nextToken() != JsonToken.END_OBJECT) { expectEvent(JsonToken.FIELD_NAME); // Do not copy current event here, each branch will take care of it String fieldName = parser.getCurrentName(); if (fieldName.equals(name)) { // Found tb.copyCurrentEvent(parser); expectNextEvent(JsonToken.VALUE_STRING); tb.copyCurrentEvent(parser); return new AbstractMap.SimpleImmutableEntry<>( parser.getText(), new JacksonJsonpParser( JsonParserSequence.createFlattened(false, tb.asParser(), parser), mapper ) ); } else { tb.copyCurrentStructure(parser); } } // Copy ending END_OBJECT tb.copyCurrentEvent(parser); } catch (IOException e) { throw JacksonUtils.convertException(e); } // Field not found return new AbstractMap.SimpleImmutableEntry<>( defaultValue, new JacksonJsonpParser( JsonParserSequence.createFlattened(false, tb.asParser(), parser), mapper ) ); } @Override public Map.Entry findVariant(Map variants) { // We're on a START_OBJECT event TokenBuffer tb = new TokenBuffer(parser, null); try { if (parser.currentToken() != JsonToken.START_OBJECT) { // Primitive value or array tb.copyCurrentStructure(parser); } else { // The resulting parser must contain the full object, including START_EVENT tb.copyCurrentEvent(parser); while (parser.nextToken() != JsonToken.END_OBJECT) { expectEvent(JsonToken.FIELD_NAME); String fieldName = parser.getCurrentName(); Variant variant = variants.get(fieldName); if (variant != null) { tb.copyCurrentEvent(parser); return new AbstractMap.SimpleImmutableEntry<>( variant, new JacksonJsonpParser( JsonParserSequence.createFlattened(false, tb.asParser(), parser), mapper ) ); } else { tb.copyCurrentStructure(parser); } } // Copy ending END_OBJECT tb.copyCurrentEvent(parser); } } catch (IOException e) { throw JacksonUtils.convertException(e); } // No variant found: return the buffered parser and let the caller decide what to do. return new AbstractMap.SimpleImmutableEntry<>( null, new JacksonJsonpParser( JsonParserSequence.createFlattened(false, tb.asParser(), parser), mapper ) ); } private void expectNextEvent(JsonToken expected) throws IOException { JsonToken event = parser.nextToken(); if (event != expected) { throw new UnexpectedJsonEventException(this, tokenToEvent.get(event), tokenToEvent.get(expected)); } } private void expectEvent(JsonToken expected) { JsonToken event = parser.currentToken(); if (event != expected) { throw new UnexpectedJsonEventException(this, tokenToEvent.get(event), tokenToEvent.get(expected)); } } //----- Buffering methods @Override public JsonData getJsonData() { try { TokenBuffer buffer = new TokenBuffer(parser); buffer.copyCurrentStructure(parser); return new JacksonJsonBuffer(buffer, mapper); } catch (IOException e) { throw JacksonUtils.convertException(e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy