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

io.protostuff.JsonInput Maven / Gradle / Ivy

There is a newer version: 1.8.0
Show newest version
//========================================================================
//Copyright 2007-2009 David Yu [email protected]
//------------------------------------------------------------------------
//Licensed 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 io.protostuff;

import static com.fasterxml.jackson.core.JsonToken.END_ARRAY;
import static com.fasterxml.jackson.core.JsonToken.END_OBJECT;
import static com.fasterxml.jackson.core.JsonToken.FIELD_NAME;
import static com.fasterxml.jackson.core.JsonToken.START_ARRAY;
import static com.fasterxml.jackson.core.JsonToken.START_OBJECT;
import static com.fasterxml.jackson.core.JsonToken.VALUE_FALSE;
import static com.fasterxml.jackson.core.JsonToken.VALUE_NULL;
import static com.fasterxml.jackson.core.JsonToken.VALUE_STRING;
import static com.fasterxml.jackson.core.JsonToken.VALUE_TRUE;

import java.io.IOException;
import java.nio.ByteBuffer;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

/**
 * An input used for reading data with json format.
 *
 * @author David Yu
 * @created Nov 20, 2009
 */
public final class JsonInput implements Input
{

    /**
     * The wrapped json parser.
     */
    public final JsonParser parser;

    /**
     * If true, the field number will be used on json keys.
     */
    public final boolean numeric;

    private boolean lastRepeated;
    private String lastName;
    private int lastNumber;

    public JsonInput(JsonParser parser)
    {
        this(parser, false);
    }

    public JsonInput(JsonParser parser, boolean numeric)
    {
        this.parser = parser;
        this.numeric = numeric;
    }

    /**
     * Returns whether the incoming messages' field names are numeric.
     */
    public boolean isNumeric()
    {
        return numeric;
    }

    /**
     * Gets the last field number read.
     */
    public int getLastNumber()
    {
        return lastNumber;
    }

    /**
     * Returns true if the last read field was a repeated field.
     */
    public boolean isLastRepeated()
    {
        return lastRepeated;
    }

    /**
     * Resets this input.
     */
    public JsonInput reset()
    {
        lastRepeated = false;
        lastName = null;
        lastNumber = 0;
        return this;
    }

    @Override
    public  void handleUnknownField(int fieldNumber, Schema schema) throws IOException
    {
        if (parser.getCurrentToken().isScalarValue())
        {
            // numeric json
            // we can skip this unknown field
            if (lastRepeated)
            {
                lastRepeated = false;
                // skip the scalar elements
                while (parser.nextToken() != END_ARRAY)
                    ;
            }
            return;
        }
        else
        {
            skipField(parser);
        }
    }

    @Override
    public  int readFieldNumber(final Schema schema) throws IOException
    {
        if (lastRepeated)
        {
            if (parser.getCurrentToken() == VALUE_NULL)
            {
                // skip null elements
                JsonToken jt;
                while (VALUE_NULL == (jt = parser.nextToken()))
                    ;

                if (jt == END_ARRAY)
                {
                    // remaining elements were all null

                    // move to the next field
                    lastRepeated = false;
                    return readFieldNumber(schema, parser);
                }
            }

            return lastNumber;
        }

        return readFieldNumber(schema, parser);
    }

    private  int readFieldNumber(final Schema schema, final JsonParser parser)
            throws IOException
    {
        for (; ; )
        {
            if (parser.nextToken() == END_OBJECT)
                return 0;

            if (parser.getCurrentToken() != FIELD_NAME)
            {
                throw new JsonInputException("Expected token: $field: but was " +
                        parser.getCurrentToken() + " on message " + schema.messageFullName());
            }

            final String name = parser.getCurrentName();

            // move to the next token
            parser.nextToken();

            // skip null value
            if (parser.getCurrentToken() == VALUE_NULL)
            {
                continue;
            }

            int number = numeric ? Integer.parseInt(name) : schema.getFieldNumber(name);

            if (number == 0)
            {
                // we can skip this unknown field
                if (!parser.getCurrentToken().isScalarValue())
                {
                    skipField(parser);
                }
                continue;
            }

            if (parser.getCurrentToken() == START_ARRAY)
            {
                JsonToken jt = parser.nextToken();

                // if empty array, read the next field
                if (jt == END_ARRAY)
                {
                    continue;
                }

                if (jt == VALUE_NULL)
                {
                    // skip null elements
                    //noinspection StatementWithEmptyBody
                    while (VALUE_NULL == (jt = parser.nextToken()))
                        ;

                    // all elements were null.
                    if (jt == END_ARRAY)
                    {
                        continue;
                    }
                }

                lastRepeated = true;
                lastName = name;
                lastNumber = number;

                return number;
            }

            lastName = name;
            lastNumber = number;

            return number;
        }
    }

    @Override
    public boolean readBool() throws IOException
    {
        final JsonToken jt = parser.getCurrentToken();
        if (lastRepeated && parser.nextToken() == END_ARRAY)
            lastRepeated = false;

        if (jt == VALUE_TRUE)
            return true;
        if (jt == VALUE_FALSE)
            return false;

        throw new JsonInputException("Expected token: true/false but was " + jt);
    }

    @Override
    public byte[] readByteArray() throws IOException
    {
        final byte[] value = parser.getBinaryValue();

        if (lastRepeated && parser.nextToken() == END_ARRAY)
            lastRepeated = false;

        return value;
    }

    @Override
    public ByteString readBytes() throws IOException
    {
        return ByteString.wrap(readByteArray());
    }

    @Override
    public double readDouble() throws IOException
    {
        final double value;
        if (parser.getCurrentToken() == VALUE_STRING)
        {
            final String textValue = parser.getText();

            if ("Infinity".equals(textValue))
                value = Double.POSITIVE_INFINITY;
            else if ("-Infinity".equals(textValue))
                    value = Double.NEGATIVE_INFINITY;
            else if ("NaN".equals(textValue))
                value = Double.NaN;
            else
                value = parser.getDoubleValue();
        } else
            value = parser.getDoubleValue();

        if (lastRepeated && parser.nextToken() == END_ARRAY)
            lastRepeated = false;

        return value;
    }

    @Override
    public int readEnum() throws IOException
    {
        return readInt32();
    }

    @Override
    public int readFixed32() throws IOException
    {
        String rawValue = parser.getText();
        if (lastRepeated && parser.nextToken() == END_ARRAY)
        {
            lastRepeated = false;
        }
        if (rawValue.startsWith("-"))
        {
            return Integer.parseInt(rawValue);
        }
        return UnsignedNumberUtil.parseUnsignedInt(rawValue);
    }

    @Override
    public long readFixed64() throws IOException
    {
        String rawValue = parser.getText();
        if (lastRepeated && parser.nextToken() == END_ARRAY)
        {
            lastRepeated = false;
        }
        if (rawValue.startsWith("-"))
        {
            return Long.parseLong(rawValue);
        }
        return UnsignedNumberUtil.parseUnsignedLong(rawValue);
    }

    @Override
    public float readFloat() throws IOException
    {
        final float value;
        if (parser.getCurrentToken() == VALUE_STRING)
        {
            final String textValue = parser.getText();

            if ("Infinity".equals(textValue))
                value = Float.POSITIVE_INFINITY;
            else if ("-Infinity".equals(textValue))
                    value = Float.NEGATIVE_INFINITY;
            else if ("NaN".equals(textValue))
                value = Float.NaN;
            else
                value = parser.getFloatValue();
        } else
            value = parser.getFloatValue();

        if (lastRepeated && parser.nextToken() == END_ARRAY)
            lastRepeated = false;

        return value;
    }

    @Override
    public int readInt32() throws IOException
    {
        final int value = parser.getIntValue();

        if (lastRepeated && parser.nextToken() == END_ARRAY)
            lastRepeated = false;

        return value;
    }

    @Override
    public long readInt64() throws IOException
    {
        final long value = parser.getLongValue();

        if (lastRepeated && parser.nextToken() == END_ARRAY)
            lastRepeated = false;

        return value;
    }

    @Override
    public int readSFixed32() throws IOException
    {
        return readInt32();
    }

    @Override
    public long readSFixed64() throws IOException
    {
        return readInt64();
    }

    @Override
    public int readSInt32() throws IOException
    {
        return readInt32();
    }

    @Override
    public long readSInt64() throws IOException
    {
        return readInt64();
    }

    @Override
    public String readString() throws IOException
    {
        if (parser.getCurrentToken() != VALUE_STRING)
            throw new JsonInputException("Expected token: string but was " + parser.getCurrentToken());

        final String value = parser.getText();

        if (lastRepeated && parser.nextToken() == END_ARRAY)
            lastRepeated = false;

        return value;
    }

    @Override
    public int readUInt32() throws IOException
    {
        String rawValue = parser.getText();
        if (lastRepeated && parser.nextToken() == END_ARRAY)
        {
            lastRepeated = false;
        }
        if (rawValue.startsWith("-"))
        {
            return Integer.parseInt(rawValue);
        }
        return UnsignedNumberUtil.parseUnsignedInt(rawValue);
    }

    @Override
    public long readUInt64() throws IOException
    {
        String rawValue = parser.getText();
        if (lastRepeated && parser.nextToken() == END_ARRAY)
        {
            lastRepeated = false;
        }
        if (rawValue.startsWith("-"))
        {
            return Long.parseLong(rawValue);
        }
        return UnsignedNumberUtil.parseUnsignedLong(rawValue);
    }

    @Override
    public  T mergeObject(T value, final Schema schema) throws IOException
    {
        if (parser.getCurrentToken() != START_OBJECT)
        {
            throw new JsonInputException("Expected token: { but was " +
                    parser.getCurrentToken() + " on " + lastName + " of message " +
                    schema.messageFullName());
        }

        final int lastNumber = this.lastNumber;
        final boolean lastRepeated = this.lastRepeated;
        final String lastName = this.lastName;

        // reset
        this.lastRepeated = false;

        if (value == null)
            value = schema.newMessage();

        schema.mergeFrom(this, value);

        if (parser.getCurrentToken() != END_OBJECT)
        {
            throw new JsonInputException("Expected token: } but was " +
                    parser.getCurrentToken() + " on " + lastName + " of message " +
                    schema.messageFullName());
        }

        if (!schema.isInitialized(value))
            throw new UninitializedMessageException(value, schema);

        // restore state
        this.lastNumber = lastNumber;
        this.lastRepeated = lastRepeated;
        this.lastName = lastName;

        if (lastRepeated && parser.nextToken() == END_ARRAY)
            this.lastRepeated = false;

        return value;
    }

    @Override
    public void transferByteRangeTo(Output output, boolean utf8String, int fieldNumber,
            boolean repeated) throws IOException
    {
        if (utf8String)
            output.writeString(fieldNumber, readString(), repeated);
        else
            output.writeByteArray(fieldNumber, readByteArray(), repeated);
    }

    /**
     * Reads a byte array/ByteBuffer value.
     */
    @Override
    public ByteBuffer readByteBuffer() throws IOException
    {
        return ByteBuffer.wrap(readByteArray());
    }

    /**
     * Skip through the entire object/array field and all nested objects/arrays inside it
     */
    private void skipField(JsonParser parser) throws IOException
    {
        int nestedObjects = 1; // we already parsed first '{' or '['
        while (nestedObjects > 0 && parser.nextToken() != null)
        {
            JsonToken token = parser.getCurrentToken();
            if (token == START_OBJECT || token == START_ARRAY)
            {
                nestedObjects++;
            }
            else if (token == END_OBJECT || token == END_ARRAY)
            {
                nestedObjects--;
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy