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

com.yahoo.document.json.JsonSerializationHelper Maven / Gradle / Ivy

The newest version!
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.yahoo.document.DataType;
import com.yahoo.document.DocumentId;
import com.yahoo.document.Field;
import com.yahoo.document.PrimitiveDataType;
import com.yahoo.document.datatypes.Array;
import com.yahoo.document.datatypes.BoolFieldValue;
import com.yahoo.document.datatypes.ByteFieldValue;
import com.yahoo.document.datatypes.CollectionFieldValue;
import com.yahoo.document.datatypes.DoubleFieldValue;
import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.FloatFieldValue;
import com.yahoo.document.datatypes.IntegerFieldValue;
import com.yahoo.document.datatypes.LongFieldValue;
import com.yahoo.document.datatypes.MapFieldValue;
import com.yahoo.document.datatypes.PredicateFieldValue;
import com.yahoo.document.datatypes.Raw;
import com.yahoo.document.datatypes.ReferenceFieldValue;
import com.yahoo.document.datatypes.StringFieldValue;
import com.yahoo.document.datatypes.Struct;
import com.yahoo.document.datatypes.StructuredFieldValue;
import com.yahoo.document.datatypes.TensorFieldValue;
import com.yahoo.document.datatypes.WeightedSet;
import com.yahoo.document.internal.GeoPosType;
import com.yahoo.document.json.readers.TensorReader;
import com.yahoo.document.json.readers.TensorRemoveUpdateReader;
import com.yahoo.document.serialization.FieldWriter;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorAddress;
import com.yahoo.tensor.TensorType;
import com.yahoo.tensor.serialization.JsonFormat;
import com.yahoo.vespa.objects.FieldBase;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Iterator;
import java.util.Map;

/**
 * @author Steinar Knutsen
 * @author Vegard Sjonfjell
 */
public class JsonSerializationHelper {

    private final static Base64.Encoder base64Encoder = Base64.getEncoder(); // Important: _basic_ format

    static class JsonSerializationException extends RuntimeException {
        public JsonSerializationException(Exception base) {
            super(base);
        }

        public JsonSerializationException(String message) {
            super(message);
        }
    }

    @FunctionalInterface
    interface SubroutineThrowingIOException {
        void invoke() throws IOException;
    }

    static void wrapIOException(SubroutineThrowingIOException lambda) {
        try {
            lambda.invoke();
        } catch (IOException e) {
            throw new JsonSerializationException(e);
        }
    }

    public static void serializeTensorField(JsonGenerator generator, FieldBase field, TensorFieldValue value,
                                            boolean shortForm, boolean directValues) {
        wrapIOException(() -> {
            fieldNameIfNotNull(generator, field);
            if (value.getTensor().isPresent()) {
                Tensor tensor = value.getTensor().get();
                byte[] encoded = JsonFormat.encode(tensor, shortForm, directValues);
                generator.writeRawValue(new String(encoded, StandardCharsets.UTF_8));
            }
            else {
                generator.writeStartObject();
                generator.writeEndObject();
            }
        });
    }

    static void serializeTensorCells(JsonGenerator generator, Tensor tensor) throws IOException {
        generator.writeArrayFieldStart(TensorReader.TENSOR_CELLS);
        for (Map.Entry cell : tensor.cells().entrySet()) {
            generator.writeStartObject();
            serializeTensorAddress(generator, cell.getKey(), tensor.type());
            generator.writeNumberField(TensorReader.TENSOR_VALUE, cell.getValue());
            generator.writeEndObject();
        }
        generator.writeEndArray();
    }

    static void serializeTensorAddresses(JsonGenerator generator, Tensor tensor) throws IOException {
        TensorType tensorType = tensor.type();
        generator.writeArrayFieldStart(TensorRemoveUpdateReader.TENSOR_ADDRESSES);
        for (Map.Entry cell : tensor.cells().entrySet()) {
            generator.writeStartObject();
            for (int i = 0; i < tensorType.dimensions().size(); i++) {
                generator.writeStringField(tensorType.dimensions().get(i).name(), cell.getKey().label(i));
            }
            generator.writeEndObject();
        }
        generator.writeEndArray();
    }

    private static void serializeTensorAddress(JsonGenerator generator, TensorAddress address, TensorType type) throws IOException {
        generator.writeObjectFieldStart(TensorReader.TENSOR_ADDRESS);
        for (int i = 0; i < type.dimensions().size(); i++)
            generator.writeStringField(type.dimensions().get(i).name(), address.label(i));
        generator.writeEndObject();
    }

    public static void serializeReferenceField(JsonGenerator generator, FieldBase field, ReferenceFieldValue value) {
        wrapIOException(() -> {
            fieldNameIfNotNull(generator, field);
            generator.writeString(value.getDocumentId().map(DocumentId::toString).orElse(""));
        });
    }

    public static void serializeStringField(JsonGenerator generator, FieldBase field, StringFieldValue value) {
        // Hide fields which only contains an empty string
        if (value.getString().length() == 0 && field != null) {
            return;
        }
        serializeString(generator, field, value.getString());
    }

    public static void serializeStructuredField(FieldWriter fieldWriter, JsonGenerator generator, FieldBase field, StructuredFieldValue value) {
        fieldNameIfNotNull(generator, field);

        wrapIOException(() -> {
            generator.writeStartObject();
            Iterator> i = value.iterator();

            while (i.hasNext()) {
                Map.Entry entry = i.next();
                entry.getValue().serialize(entry.getKey(), fieldWriter);
            }

            generator.writeEndObject();
        });
    }

    private static void serializeGeoPos(JsonGenerator generator, FieldBase field, Struct value, GeoPosType dataType) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> {
                generator.writeStartObject();
                generator.writeFieldName("lat");
                generator.writeRawValue(dataType.fmtLatitude(value));
                generator.writeFieldName("lng");
                generator.writeRawValue(dataType.fmtLongitude(value));
                generator.writeEndObject();
        });
    }

    public static void serializeStructField(FieldWriter fieldWriter, JsonGenerator generator, FieldBase field, Struct value) {
        DataType dt = value.getDataType();
        if (dt instanceof GeoPosType gpt) {
            if (gpt.renderJsonAsVespa8()) {
                serializeGeoPos(generator, field, value, gpt);
                return;
            }
        }
        serializeStructuredField(fieldWriter, generator, field, value);
    }

    public static  void serializeWeightedSet(JsonGenerator generator, FieldBase field, WeightedSet value) {
        // Hide empty fields
        if (value.size() == 0) {
            return;
        }
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> {
            generator.writeStartObject();

            for (T key : value.keySet()) {
                Integer weight = value.get(key);
                // key.toString() is according to spec
                generator.writeNumberField(key.toString(), weight);
            }

            generator.writeEndObject();
        });
    }

    public static  void serializeCollectionField(FieldWriter fieldWriter, JsonGenerator generator, FieldBase field, CollectionFieldValue value) {
        // Hide empty fields
        if (value.size() == 0) {
            return;
        }
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> {
            generator.writeStartArray();
            Iterator i = value.iterator();

            while (i.hasNext()) {
                i.next().serialize(null, fieldWriter);
            }

            generator.writeEndArray();
        });
    }


    public static  void serializeMapField(FieldWriter fieldWriter, JsonGenerator generator, FieldBase field, MapFieldValue map) {
        // Hide empty fields
        if (map.size() == 0) {
            return;
        }
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> {
            generator.writeStartObject();

            for (Map.Entry entry : map.entrySet()) {
                K key = entry.getKey();
                DataType keyType = key.getDataType();
                if ( ! (keyType instanceof PrimitiveDataType)) {
                    throw new IllegalArgumentException("Can't use complex types as keys for map fields. Type: " + keyType);
                }
                generator.writeFieldName(key.toString());
                entry.getValue().serialize(null, fieldWriter);
            }

            generator.writeEndObject();
        });
    }

    public static  void serializeArrayField(FieldWriter fieldWriter, JsonGenerator generator, FieldBase field, Array value) {
        // Hide empty fields
        if (value.size() == 0) {
            return;
        }
        wrapIOException(() -> {
            fieldNameIfNotNull(generator, field);
            generator.writeStartArray();

            for (T elem : value) {
                elem.serialize(null, fieldWriter);
            }

            generator.writeEndArray();
        });
    }

    public static void serializeDoubleField(JsonGenerator generator, FieldBase field, DoubleFieldValue value) {
        serializeDouble(generator, field, value.getDouble());
    }

    public static void serializeFloatField(JsonGenerator generator, FieldBase field, FloatFieldValue value) {
        serializeFloat(generator, field, value.getFloat());
    }

    public static void serializeIntField(JsonGenerator generator, FieldBase field, IntegerFieldValue value) {
        serializeInt(generator, field, value.getInteger());
    }

    public static void serializeLongField(JsonGenerator generator, FieldBase field, LongFieldValue value) {
        serializeLong(generator, field, value.getLong());
    }

    public static void serializeByteField(JsonGenerator generator, FieldBase field, ByteFieldValue value) {
        serializeByte(generator, field, value.getByte());
    }

    public static void serializeBoolField(JsonGenerator generator, FieldBase field, BoolFieldValue value) {
        serializeBool(generator, field, value.getBoolean());
    }

    public static void serializePredicateField(JsonGenerator generator, FieldBase field, PredicateFieldValue value){
        serializeString(generator, field, value.toString());
    }

    public static void serializeRawField(JsonGenerator generator, FieldBase field, Raw raw) {
        serializeByteBuffer(generator, field, raw.getByteBuffer());
    }

    public static void serializeString(JsonGenerator generator, FieldBase field, String value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeString(value));
    }

    public static void serializeByte(JsonGenerator generator, FieldBase field,  byte value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeNumber(value));
    }

    public static void serializeBool(JsonGenerator generator, FieldBase field,  boolean value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeBoolean(value));
    }

    public static void serializeShort(JsonGenerator generator, FieldBase field, short value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeNumber(value));
    }

    public static void serializeInt(JsonGenerator generator, FieldBase field, int value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeNumber(value));
    }

    public static void serializeLong(JsonGenerator generator, FieldBase field, long value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeNumber(value));
    }

    public static void serializeFloat(JsonGenerator generator, FieldBase field, float value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeNumber(value));
    }

    public static void serializeDouble(JsonGenerator generator, FieldBase field, double value) {
        fieldNameIfNotNull(generator, field);
        wrapIOException(() -> generator.writeNumber(value));
    }

    public static void serializeByteBuffer(JsonGenerator generator, FieldBase field, ByteBuffer raw) {
        fieldNameIfNotNull(generator, field);

        final byte[] data = new byte[raw.remaining()];
        final int origPosition = raw.position();

        // base64encoder has no encode methods with offset and
        // limit, so no use trying to get at the backing array if
        // available anyway
        raw.get(data);
        raw.position(origPosition);

        wrapIOException(() -> generator.writeString(base64Encoder.encodeToString(data)));
    }

    public static void serializeByteArray(JsonGenerator generator, FieldBase field, byte[] value) {
        serializeByteBuffer(generator, field, ByteBuffer.wrap(value));
    }

    public static void fieldNameIfNotNull(JsonGenerator generator, FieldBase field) {
        if (field != null) {
            wrapIOException(() -> generator.writeFieldName(field.getName()));
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy