com.epam.deltix.qsrv.util.json.JSONRawMessageParser Maven / Gradle / Ivy
/*
* Copyright 2024 EPAM Systems, Inc
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. 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 com.epam.deltix.qsrv.util.json;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.epam.deltix.dfp.Decimal64Utils;
import com.epam.deltix.qsrv.hf.pub.RawMessage;
import com.epam.deltix.qsrv.hf.pub.UnboundWriter;
import com.epam.deltix.qsrv.hf.pub.WritableValue;
import com.epam.deltix.qsrv.hf.pub.codec.CodecMetaFactory;
import com.epam.deltix.qsrv.hf.pub.codec.CompiledCodecMetaFactory;
import com.epam.deltix.qsrv.hf.pub.codec.FixedUnboundEncoder;
import com.epam.deltix.qsrv.hf.pub.codec.UnboundEncoder;
import com.epam.deltix.qsrv.hf.pub.md.*;
import com.epam.deltix.util.collections.generated.ByteArrayList;
import com.epam.deltix.util.collections.generated.ObjectToObjectHashMap;
import com.epam.deltix.util.memory.MemoryDataOutput;
import com.epam.deltix.util.time.TimeFormatter;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;
/**
* Json parser into InstrumentMessage message.
*
* This class is not thread-safe.
*
*
Multiple threads should not access the same instance of this class concurrently
* as it may result in unexpected behavior or data corruption.
*/
public class JSONRawMessageParser extends UnboundWriter {
private final ObjectToObjectHashMap encoders = new ObjectToObjectHashMap<> ();
private final CodecMetaFactory factory = CompiledCodecMetaFactory.INSTANCE;
private final RecordClassDescriptor[] descriptors;
private final RawMessage raw = new RawMessage();
private final MemoryDataOutput out = new MemoryDataOutput();
private final DateFormatter dateFormatter = new DateFormatter();
private DecimalFormat decimalFormat;
private String decimalSeparator;
public JSONRawMessageParser(RecordClassDescriptor[] descriptors) {
this(descriptors, "type");
}
public JSONRawMessageParser(RecordClassDescriptor[] descriptors, String typePropertyName) {
this(descriptors, typePropertyName, Locale.getDefault(Locale.Category.FORMAT));
}
public JSONRawMessageParser(RecordClassDescriptor[] descriptors, String typePropertyName, Locale locale) {
super(typePropertyName);
this.descriptors = descriptors;
decimalFormat = new DecimalFormat("#.#", DecimalFormatSymbols.getInstance(locale));
updateDecimalSeparator();
}
private void updateDecimalSeparator() {
decimalSeparator = Character.toString(decimalFormat.getDecimalFormatSymbols().getDecimalSeparator());
}
public void setDecimalFormat(DecimalFormat decimalFormat) {
this.decimalFormat = decimalFormat;
updateDecimalSeparator();
}
private FixedUnboundEncoder getEncoder (final RecordClassDescriptor type) {
String guid = type.getGuid ();
FixedUnboundEncoder encoder = encoders.get (guid, null);
if (encoder == null) {
encoder = factory.createFixedUnboundEncoderFactory (type).create ();
encoders.put (guid, encoder);
}
return encoder;
}
@Override
public void writeField(JsonElement value, DataType type, WritableValue w) {
if (value == null || value.isJsonNull())
w.writeNull();
else if (type instanceof IntegerDataType)
w.writeLong(value.getAsLong());
else if (type instanceof FloatDataType) {
if (((FloatDataType) type).isDecimal64()) {
w.writeLong(Decimal64Utils.parse(value.getAsString(), decimalSeparator));
} else if (((FloatDataType) type).isFloat()) {
w.writeFloat(parseNumber(value.getAsString()).floatValue());
} else {
w.writeDouble(parseNumber(value.getAsString()).doubleValue());
}
}
else if (type instanceof VarcharDataType || type instanceof CharDataType) {
w.writeString(value.getAsString());
}
else if (type instanceof EnumDataType) {
if (value.isJsonPrimitive()) {
JsonPrimitive p = (JsonPrimitive) value;
if (p.isNumber())
w.writeLong(p.getAsLong());
if (p.isString())
w.writeString(p.getAsString());
}
else
throw new IllegalArgumentException("Expected primitive value: " + value.getClass().getName());
}
else if (type instanceof BooleanDataType) {
w.writeBoolean(value.getAsBoolean());
}
else if (type instanceof DateTimeDataType) {
long time = parseDateTime(value.getAsString(), (DateTimeDataType) type);
w.writeLong(time);
}
else if (type instanceof TimeOfDayDataType) {
int time = parseTime(value.getAsString());
w.writeInt(time);
} else if (type instanceof ArrayDataType) {
writeArray(value, (ArrayDataType) type, w);
} else if (type instanceof ClassDataType) {
RecordClassDescriptor rcd = matchObjectType(value, (ClassDataType) type);
writeObject(value, w.getFieldEncoder(rcd));
} else if (type instanceof BinaryDataType) {
ByteArrayList list = new ByteArrayList();
for (JsonElement jsonElement : value.getAsJsonArray()) {
list.add(jsonElement.getAsByte());
}
w.writeBinary(list.getInternalBuffer(), 0, list.size());
} else
throw new RuntimeException("Unrecognized DataType: " + type);
}
protected long parseTimestamp(String value) {
try {
return dateFormatter.nanoFromDateString(value);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
protected long parseDateTime(String value, DateTimeDataType type) {
try {
if (type.hasNanosecondPrecision()) {
return dateFormatter.nanoFromDateString(value);
} else {
return dateFormatter.msFromDateString(value);
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
protected int parseTime(String value) {
return TimeFormatter.parseTimeOfDayMillis(value);
}
private Number parseNumber(String strValue) {
try {
return decimalFormat.parse(strValue);
} catch (ParseException e) {
throw new IllegalArgumentException("Failed to convert the '" + strValue + "' to a numeric value", e);
}
}
@Override
protected Object getObjectType(JsonElement value) {
JsonElement element = ((JsonObject) value).get(typePropertyName);
return element != null ? element.getAsString() : null;
}
@Override
public void writeArray(JsonElement value, ArrayDataType type, WritableValue w) {
if (value instanceof JsonArray) {
JsonArray array = ((JsonArray)value);
writeArray(array, array.size(), type, w);
} else {
super.writeArray(value, type, w);
}
}
public void writeObject(JsonElement value, UnboundEncoder encoder) {
if (value instanceof JsonObject) {
JsonObject obj = (JsonObject)value;
while (encoder.nextField()) {
JsonElement v = obj.get(encoder.getField().getName());
if (v != null)
writeField(v, encoder.getField().getType(), encoder);
}
} else {
throw new RuntimeException("Unrecognized DataType: " + value);
}
}
public RawMessage parse(JsonObject object) {
RecordClassDescriptor msgType = matchObjectType(object, descriptors);
FixedUnboundEncoder encoder = getEncoder(msgType);
out.reset();
encoder.beginWrite(out);
writeObject(object, encoder);
encoder.endWrite();
raw.type = msgType;
raw.setBytes(out);
if (object.has("symbol"))
raw.setSymbol(object.get("symbol").getAsString());
else
raw.setSymbol("");
if (object.has("timestamp")) {
raw.setNanoTime(parseTimestamp(object.get("timestamp").getAsString()));
} else {
raw.setTimeStampMs(Long.MIN_VALUE);
}
if (object.has("nanoTime")) {
raw.setNanoTime(parseTimestamp(object.get("nanoTime").getAsString()));
}
return raw;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy