Skip to content

Latest commit

 

History

History
366 lines (280 loc) · 11.1 KB

README.md

File metadata and controls

366 lines (280 loc) · 11.1 KB


Opack

Opack is a Java library that can serialize/deserialize between Java objects and common objects(OpackValue). Also, common objects can be encoded or decoded as JSON or Bytes(Dense).

We faster then GSON and Kryo. (See tests)

Click to see performance result
# GsonPerformanceTest
  Gson  : 7094ms
  Opack : 2613ms

# KryoPerformanceTest
 Kryo  : 7227ms
 Opack : 2304ms

# JacksonPerformanceTest
 Jackson: 6412ms
 Opack  : 6404ms

Simple flow

Download

Gradle:

dependencies {
  implementation 'com.realtimetech:opack:0.0.12'
}

Maven:

<dependency>
    <groupId>com.realtimetech</groupId>
    <artifactId>opack</artifactId>
    <version>0.0.12</version>
</dependency>

Usage

1. Serialize

public class Usage {
    public static void main(String[] arguments) {
        Opacker opacker = new Opacker.Builder().create();

        SomeObject someObject = new SomeObject();

        OpackValue opackValue = opacker.serialize(someObject);
    }
}

2. Deserialize

public class Usage {
    public static void main(String[] arguments) {
        Opacker opacker = new Opacker.Builder()
                .setContextStackInitialSize(128)                    // (Optional) Creation size of stack for processing
                .setValueStackInitialSize(512)                      // (Optional) Creation size of stack for processing

                .setEnableWrapListElementType(false)                // (Optional) When converting elements of a list, record the type as well
                .setEnableWrapMapElementType(false)                 // (Optional) When converting elements of a map, record the type as well
                .setEnableConvertEnumToOrdinal(false)               // (Optional) Convert enum to ordinal or name
                .setEnableConvertRecursiveDependencyToNull(false)   // (Optional) Convert recursive depandency, record null
                .create();

        OpackValue serializedSomeObject = null;

        SomeObject someObject = opacker.deserialize(SomeObject.class, serializedSomeObject);
    }
}

3. Json Codec

public class Usage {
    public static void main(String[] arguments) {
        JsonCodec jsonCodec = new JsonCodec.Builder()
                .setEncodeStackInitialSize(128)             // (Optional) Creation size of stack for processing
                .setEncodeStringBufferSize(1024)            // (Optional) Creation size of stack for processing
                .setDecodeStackInitialSize(128)             // (Optional) Creation size of stack for processing

                .setAllowOpackValueToKeyValue(false)        // (Optional) Accepts Object or Array as Key of Json Object
                .setEnableConvertCharacterToString(false)   // (Optional) Convert character to string instead of character int value
                .setUsePrettyFormat(false)                  // (Optional) When encoding, it prints formatted

                .create();

        OpackValue opackValue;

        // Encode Basic
        String json = jsonCodec.encode(opackValue);

        // Encode with Java IO Writer
        Writer writer;
        jsonCodec.encode(writer, opackValue);

        // Decode Basic
        OpackValue decodedOpackValue = jsonCodec.decode(json);
    }
}

4. Dense Codec

public class Usage {
    public static void main(String[] arguments) {
        DenseCodec denseCodec = new DenseCodec.Builder()
                .setEncodeStackInitialSize(128)         // (Optional) Creation size of stack for processing
                .setDecodeStackInitialSize(128)         // (Optional) Creation size of stack for processing

                .setIgnoreVersionCompare(false)         // (Optional) Ignore compare dense codec version in data

                .create();

        OpackValue opackValue;

        // Encode Basic
        byte[] bytes = denseCodec.encode(opackValue);

        // Encode with Java IO OutputStream
        OutputStream outputStream;
        denseCodec.encode(OutputStreamWriter.of(outputStream), opackValue);

        // Encode with ByteArrayWriter
        ByteArrayWriter byteArrayWriter = new ByteArrayWriter();
        denseCodec.encode(byteArrayWriter, opackValue);
        byte[] bytes = byteArrayWriter.toByteArray();

        // Decode Basic
        OpackValue decodedOpackValue = denseCodec.decode(bytes);

        // Decode with Java IO InputStream
        InputStream inputStream;
        OpackValue decodedOpackValue = denseCodec.decode(InputStreamReader.of(inputStream));

        // Decode with ByteArrayReader
        ByteArrayReader byteArrayReader = new ByteArrayReader(bytes);
        OpackValue decodedOpackValue = denseCodec.decode(byteArrayReader);
    }
}

Advanced Usage

1. Ignore and Type and Name

public class SomeObject {
    private String stringField;
    private byte[] bytesField;

    // This field will not serialize/deserialize
    @Ignore
    private String verySecretField;

    // This field will serialize/deserialize to explicit type `ArrayList` instead of ambiguous field type `List`
    @Type(ArrayList.class)
    private List<String> listField;
 
    // This field will serialize/deserialize to `newFieldName` name instead of actual field name `oldFieldName`
    @Name("newFieldName")
    private String oldFieldName;
}

2. Field Transformer

public class ByteToBase64Transformer implements Transformer {
    @Override
    public Object serialize(Opacker opacker, Object value) throws SerializeException {
        if (value instanceof byte[]) {
            return Base64.getEncoder().encodeToString((byte[]) value);
        }

        return value;
    }

    @Override
    public Object deserialize(Opacker opacker, Class<?> goalType, Object value) throws DeserializeException {
        if (value instanceof String) {
            return Base64.getDecoder().decode((String) value);
        }

        return value;
    }
}

public class SomeObject {
    // This field will serialize/deserialize to Base64
    @Transform(transformer = ByteToBase64Transformer.class)
    private byte[] bytesField;
}

3. Field With Type

public class SomeObject {
    // This field will serialize with runtime type, and deserialize actual type instead of ambiguous field type `List`
    @WithType
    private List<String> stringListField;

    // This field will serialize with runtime type, and deserialize actual type instead of ambiguous field type `Object`
    @WithType
    private Object[] objectArrayField;
}

4. Class Transformer

public class AnimalTransformer implements Transformer {
    // Remove a `sound` from a serialized `Animal`
    @Override
    public Object serialize(Opacker opacker, Object value) throws SerializeException {
        if (value instanceof Animal) {
            Animal animal = (Animal) value;
            OpackValue opackValue = opacker.serialize(animal);

            if (opackValue instanceof OpackObject) {
                OpackObject opackObject = (OpackObject) opackValue;
                opackObject.remove("sound");
                return opackObject;
            }
        }

        return value;
    }

    // Restore `sound` from `Animal` before deserialization
    @Override
    public Object deserialize(Opacker opacker, Class<?> goalType, Object value) throws DeserializeException {
        if (value instanceof OpackObject) {
            if (Animal.class.isAssignableFrom(goalType)) {
                OpackObject opackObject = (OpackObject) value;
                Animal animal = (Animal) opacker.deserialize(goalType, opackObject);
                animal.setSound(animal.bark());
            }
        }

        return value;
    }
}

// When `inheritable` is set to true, it applies to child classes.
@Transform(transformer = AnimalTransformer.class, inheritable = true)
abstract class Animal {
    private String sound;

    public abstract String bark();

    public String getSound() {
        return sound;
    }

    public void setSound(String sound) {
        this.sound = sound;
    }
}

public class Dog extends Animal {
    @Override
    public String bark() {
        return "Bow-Wow";
    }
}

public class SomeObject {
    private Dog dogField;
}

5. Handling Opack Value

public class Usage {
    public static void main(String[] arguments) {
        OpackObject<String, OpackValue> rootObject = new OpackObject<>();

        {
            OpackArray<Integer> opackArray = new OpackArray<>();
            opackArray.add(Integer.MAX_VALUE);
            rootObject.put("array", opackArray);
        }

        {
            OpackArray<?> opackArray = OpackArray.createWithArrayObject(new int[]{1, 2, 3, 4, 5, 6});
            rootObject.put("unmodifiable(but, really fast) array", opackArray);
        }

        {
            OpackObject<Object, Object> opackObject = new OpackObject<>();
            opackObject.put("int", 1);
            opackObject.put("float", 1.1f);
            opackObject.put("long", Long.MAX_VALUE);
            opackObject.put("double", 1.1d);

            opackObject.put(1024, "2^10");
            opackObject.put(
                    OpackArray.createWithArrayObject(new byte[]{1, 2, 3, 4, 5}),
                    "a lot of bytes"
            );

            rootObject.put("number_map", opackObject);
        }

        OpackArray<Integer> opackArray = (OpackArray) rootObject.get("array");
        OpackObject<Object, Object> opackObject = (OpackObject) rootObject.get("number_map");

        System.out.println("1024 is " + (opackObject.get(1024)));
        System.out.println("Array length is " + (opackArray.length()));
        System.out.println("First element is " + (opackArray.get(0)));
    }
}

To-Do

  • Separate field transformer and class transformer
  • Add generic into transformer for type safety
  • Add field pre/post transformer
  • Remove fieldTransformer argument of Opacker.prepareObjectDeserialize
  • Remove withType argument of Opacker.prepareObjectDeserialize

License

Opack uses Apache License 2.0. Please, leave your feedback if you have any suggestions!

Jeonghwan, Park
+821032735003
dev.parkjeonghwan@gmail.com