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

org.opensearch.common.xcontent.JsonToStringXContentParser Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

package org.opensearch.common.xcontent;

import org.opensearch.common.xcontent.json.JsonXContent;
import org.opensearch.core.common.bytes.BytesReference;
import org.opensearch.core.xcontent.AbstractXContentParser;
import org.opensearch.core.xcontent.DeprecationHandler;
import org.opensearch.core.xcontent.MediaType;
import org.opensearch.core.xcontent.MediaTypeRegistry;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentLocation;
import org.opensearch.core.xcontent.XContentParser;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.CharBuffer;
import java.util.ArrayList;

/**
 * JsonToStringParser is the main parser class to transform JSON into stringFields in a XContentParser
 * returns XContentParser with one parent field and subfields
 * fieldName, fieldName._value, fieldName._valueAndPath
 * @opensearch.internal
 */
public class JsonToStringXContentParser extends AbstractXContentParser {
    private final String fieldTypeName;
    private XContentParser parser;

    private ArrayList valueList = new ArrayList<>();
    private ArrayList valueAndPathList = new ArrayList<>();
    private ArrayList keyList = new ArrayList<>();

    private XContentBuilder builder = XContentBuilder.builder(JsonXContent.jsonXContent);

    private NamedXContentRegistry xContentRegistry;

    private DeprecationHandler deprecationHandler;

    private static final String VALUE_AND_PATH_SUFFIX = "._valueAndPath";
    private static final String VALUE_SUFFIX = "._value";
    private static final String DOT_SYMBOL = ".";
    private static final String EQUAL_SYMBOL = "=";

    public JsonToStringXContentParser(
        NamedXContentRegistry xContentRegistry,
        DeprecationHandler deprecationHandler,
        XContentParser parser,
        String fieldTypeName
    ) throws IOException {
        super(xContentRegistry, deprecationHandler);
        this.deprecationHandler = deprecationHandler;
        this.xContentRegistry = xContentRegistry;
        this.parser = parser;
        this.fieldTypeName = fieldTypeName;
    }

    public XContentParser parseObject() throws IOException {
        builder.startObject();
        StringBuilder path = new StringBuilder(fieldTypeName);
        parseToken(path, null);
        builder.field(this.fieldTypeName, keyList);
        builder.field(this.fieldTypeName + VALUE_SUFFIX, valueList);
        builder.field(this.fieldTypeName + VALUE_AND_PATH_SUFFIX, valueAndPathList);
        builder.endObject();
        String jString = XContentHelper.convertToJson(BytesReference.bytes(builder), false, MediaTypeRegistry.JSON);
        return JsonXContent.jsonXContent.createParser(this.xContentRegistry, this.deprecationHandler, String.valueOf(jString));
    }

    private void parseToken(StringBuilder path, String currentFieldName) throws IOException {

        while (this.parser.nextToken() != Token.END_OBJECT) {
            if (this.parser.currentName() != null) {
                currentFieldName = this.parser.currentName();
            }
            StringBuilder parsedFields = new StringBuilder();

            if (this.parser.currentToken() == Token.FIELD_NAME) {
                path.append(DOT_SYMBOL).append(currentFieldName);
                int dotIndex = currentFieldName.indexOf(DOT_SYMBOL);
                String fieldNameSuffix = currentFieldName;
                // The field name may be of the form foo.bar.baz
                // If that's the case, each "part" is a key.
                while (dotIndex >= 0) {
                    String fieldNamePrefix = fieldNameSuffix.substring(0, dotIndex);
                    if (!fieldNamePrefix.isEmpty()) {
                        this.keyList.add(fieldNamePrefix);
                    }
                    fieldNameSuffix = fieldNameSuffix.substring(dotIndex + 1);
                    dotIndex = fieldNameSuffix.indexOf(DOT_SYMBOL);
                }
                if (!fieldNameSuffix.isEmpty()) {
                    this.keyList.add(fieldNameSuffix);
                }
            } else if (this.parser.currentToken() == Token.START_ARRAY) {
                parseToken(path, currentFieldName);
                break;
            } else if (this.parser.currentToken() == Token.END_ARRAY) {
                // skip
            } else if (this.parser.currentToken() == Token.START_OBJECT) {
                parseToken(path, currentFieldName);
                int dotIndex = path.lastIndexOf(DOT_SYMBOL, path.length());

                if (dotIndex != -1 && path.length() > currentFieldName.length()) {
                    path.setLength(path.length() - currentFieldName.length() - 1);
                }
            } else {
                if (!path.toString().contains(currentFieldName)) {
                    path.append(DOT_SYMBOL).append(currentFieldName);
                }
                parseValue(parsedFields);
                this.valueList.add(parsedFields.toString());
                this.valueAndPathList.add(path + EQUAL_SYMBOL + parsedFields);
                int dotIndex = path.lastIndexOf(DOT_SYMBOL, path.length());
                if (dotIndex != -1 && path.length() > currentFieldName.length()) {
                    path.setLength(path.length() - currentFieldName.length() - 1);
                }
            }

        }
    }

    private void parseValue(StringBuilder parsedFields) throws IOException {
        switch (this.parser.currentToken()) {
            case VALUE_BOOLEAN:
            case VALUE_NUMBER:
            case VALUE_STRING:
            case VALUE_NULL:
                parsedFields.append(this.parser.textOrNull());
                break;
            // Handle other token types as needed
            case FIELD_NAME:
            case VALUE_EMBEDDED_OBJECT:
            case END_ARRAY:
            case START_ARRAY:
                break;
            default:
                throw new IOException("Unsupported token type [" + parser.currentToken() + "]");
        }
    }

    @Override
    public MediaType contentType() {
        return MediaTypeRegistry.JSON;
    }

    @Override
    public Token nextToken() throws IOException {
        return this.parser.nextToken();
    }

    @Override
    public void skipChildren() throws IOException {
        this.parser.skipChildren();
    }

    @Override
    public Token currentToken() {
        return this.parser.currentToken();
    }

    @Override
    public String currentName() throws IOException {
        return this.parser.currentName();
    }

    @Override
    public String text() throws IOException {
        return this.parser.text();
    }

    @Override
    public CharBuffer charBuffer() throws IOException {
        return this.parser.charBuffer();
    }

    @Override
    public Object objectText() throws IOException {
        return this.parser.objectText();
    }

    @Override
    public Object objectBytes() throws IOException {
        return this.parser.objectBytes();
    }

    @Override
    public boolean hasTextCharacters() {
        return this.parser.hasTextCharacters();
    }

    @Override
    public char[] textCharacters() throws IOException {
        return this.parser.textCharacters();
    }

    @Override
    public int textLength() throws IOException {
        return this.parser.textLength();
    }

    @Override
    public int textOffset() throws IOException {
        return this.parser.textOffset();
    }

    @Override
    public Number numberValue() throws IOException {
        return this.parser.numberValue();
    }

    @Override
    public NumberType numberType() throws IOException {
        return this.parser.numberType();
    }

    @Override
    public byte[] binaryValue() throws IOException {
        return this.parser.binaryValue();
    }

    @Override
    public XContentLocation getTokenLocation() {
        return this.parser.getTokenLocation();
    }

    @Override
    protected boolean doBooleanValue() throws IOException {
        return this.parser.booleanValue();
    }

    @Override
    protected short doShortValue() throws IOException {
        return this.parser.shortValue();
    }

    @Override
    protected int doIntValue() throws IOException {
        return this.parser.intValue();
    }

    @Override
    protected long doLongValue() throws IOException {
        return this.parser.longValue();
    }

    @Override
    protected float doFloatValue() throws IOException {
        return this.parser.floatValue();
    }

    @Override
    protected double doDoubleValue() throws IOException {
        return this.parser.doubleValue();
    }

    @Override
    protected BigInteger doBigIntegerValue() throws IOException {
        return this.parser.bigIntegerValue();
    }

    @Override
    public boolean isClosed() {
        return this.parser.isClosed();
    }

    @Override
    public void close() throws IOException {
        this.parser.close();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy