
io.protostuff.JsonInput Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protostuff-json Show documentation
Show all versions of protostuff-json Show documentation
protostuff serialization using json format
//========================================================================
//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