https://github.com/realtimetech-solution/opack
Fast object or data serialize and deserialize library
https://github.com/realtimetech-solution/opack
cloning java json serialization
Last synced: 5 months ago
JSON representation
Fast object or data serialize and deserialize library
- Host: GitHub
- URL: https://github.com/realtimetech-solution/opack
- Owner: realtimetech-solution
- License: apache-2.0
- Created: 2021-12-03T02:40:42.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2025-03-17T09:35:24.000Z (over 1 year ago)
- Last Synced: 2025-03-17T10:39:11.213Z (over 1 year ago)
- Topics: cloning, java, json, serialization
- Language: Java
- Homepage:
- Size: 867 KB
- Stars: 28
- Watchers: 5
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README

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 than GSON and Kryo and Jackson.** (See [tests](./src/test/java/com/realtimetech/opack/test/performance))
Click to see performance benchmark result
```
# GsonPerformanceTest
Gson(T) : 4441ms
Gson(D) : 2839ms
Opack : 2756ms
# KryoPerformanceTest
Kryo : 3110ms
Opack : 688ms
# JacksonPerformanceTest
Jackson : 4626ms
Opack : 3750ms
```
### Simple flow
### Download
Gradle:
```gradle
dependencies {
implementation 'com.realtimetech:opack:0.2.1'
}
```
Maven:
```xml
com.realtimetech
opack
0.2.1
```
### Usage
#### 1. Serialize
```java
public class Usage {
public static void main(String[] arguments) {
Opacker opacker = Opacker.Builder.create().build();
SomeObject someObject = new SomeObject();
OpackValue opackValue = opacker.serialize(someObject);
}
}
```
#### 2. Deserialize
```java
public class Usage {
public static void main(String[] arguments) {
Opacker opacker = Opacker.Builder.create()
.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
.setClassLoader(Usage.class.getClassLoader()) // (Optional) Class loader for processing
.build();
OpackValue serializedSomeObject = null;
SomeObject someObject = opacker.deserialize(SomeObject.class, serializedSomeObject);
}
}
```
#### 3. Json Codec
##### General Usage
```java
public class Usage {
public static void main(String[] arguments) {
JsonCodec jsonCodec = JsonCodec.Builder.create()
.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
.setAllowAnyValueToKey(false) // (Optional) Accepts non-string value 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
.setRoundingMode(RoundingMode.ROUND_EVEN) // (Optional) Rounding mode to be used for double string conversion
.build();
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);
}
}
```
##### Easy Usage
```java
public class Usage {
public static void main(String[] arguments) {
OpackValue opackValue;
// Encode
String json = Json.encode(opackValue);
// Decode
OpackValue decodedOpackValue = Json.decode(json);
}
}
```
#### 4. Dense Codec
```java
public class Usage {
public static void main(String[] arguments) {
DenseCodec denseCodec = DenseCodec.Builder.create()
.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
.build();
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
```java
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 listField;
// This field will serialize/deserialize to `newFieldName` name instead of actual field name `oldFieldName`
@Name("newFieldName")
private String oldFieldName;
}
```
#### 2. Field Transformer
```java
public class ByteToBase64Transformer implements Transformer {
@Override
public @Nullable Object serialize(@NotNull Opacker opacker, @NotNull Class> originalType, @Nullable Object object) throws SerializeException {
if (object instanceof byte[]) {
return Base64.getEncoder().encodeToString((byte[]) object);
}
return object;
}
@Override
public @Nullable Object deserialize(@NotNull Opacker opacker, @NotNull Class> goalType, @Nullable Object object) throws DeserializeException {
if (object instanceof String) {
return Base64.getDecoder().decode((String) object);
}
return object;
}
}
public class SomeObject {
// This field will serialize/deserialize to Base64
@Transform(transformer = ByteToBase64Transformer.class)
private byte[] bytesField;
}
```
#### 3. Field With Type
```java
public class SomeObject {
// This field will serialize with runtime type, and deserialize actual type instead of ambiguous field type `List`
@WithType
private List 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
```java
public class AnimalTransformer implements Transformer {
// Remove a `sound` from a serialized `Animal`
@Override
public @Nullable Object serialize(@NotNull Opacker opacker, @NotNull Class> originalType, @Nullable Object object) throws SerializeException {
if (object instanceof Animal) {
Animal animal = (Animal) object;
OpackValue opackValue = opacker.serialize(animal);
if (opackValue instanceof OpackObject) {
OpackObject opackObject = (OpackObject) opackValue;
opackObject.remove("sound");
return opackObject;
}
}
return object;
}
// Restore `sound` from `Animal` before deserialization
@Override
public @Nullable Object deserialize(@NotNull Opacker opacker, @NotNull Class> goalType, @Nullable Object object) throws DeserializeException {
if (object instanceof OpackObject) {
if (Animal.class.isAssignableFrom(goalType)) {
OpackObject opackObject = (OpackObject) object;
Animal animal = (Animal) opacker.deserialize(goalType, opackObject);
animal.setSound(animal.bark());
}
}
return object;
}
}
// 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
```java
public class Usage {
public static void main(String[] arguments) {
OpackObject rootObject = new OpackObject();
{
OpackArray 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 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 opackArray = (OpackArray) rootObject.get("array");
OpackObject 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 the 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](./LICENSE). Please leave your feedback if you have any suggestions!
```
Jeonghwan, Park
+821032735003
dev.parkjeonghwan@gmail.com
```

