-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).
+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](./src/test/java/com/realtimetech/opack/test/performance))
@@ -28,22 +29,23 @@ Opack is a Java library that can serialize/deserialize between Java objects and
Jackson: 6412ms
Opack : 6404ms
```
-
+
### Simple flow
+
-
+
-
### Download
Gradle:
+
```gradle
dependencies {
implementation 'com.realtimetech:opack:0.0.9'
@@ -51,103 +53,112 @@ dependencies {
```
Maven:
+
```xml
+
- com.realtimetech
- opack
- 0.0.9
+ com.realtimetech
+ opack
+ 0.0.9
```
### Usage
+
#### 1. Serialize
+
```java
-Opacker opacker = new Opacker.Builder().create();
+Opacker opacker=new Opacker.Builder().create();
-SomeObject someObject = new SomeObject();
+ SomeObject someObject=new SomeObject();
-OpackValue opackValue = opacker.serialize(someObject);
+ OpackValue opackValue=opacker.serialize(someObject);
```
#### 2. Deserialize
+
```java
-Opacker opacker = new Opacker.Builder()
+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 = /** See Serialize Usage **/;
+ OpackValue serializedSomeObject= /** See Serialize Usage **/;
-SomeObject someObject = opacker.deserialize(SomeObject.class, serializedSomeObject);
+ SomeObject someObject=opacker.deserialize(SomeObject.class,serializedSomeObject);
```
#### 3. Json Codec
+
```java
-JsonCodec jsonCodec = new JsonCodec.Builder()
+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 Objct 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 = /** See Serialize Usage **/;
+ OpackValue opackValue= /** See Serialize Usage **/;
/*
Encode
*/
-String json = jsonCodec.encode(opackValue);
+ String json=jsonCodec.encode(opackValue);
// Or
-Writer writer = new StringWriter();
-jsonCodec.encode(writer, opackValue);
+ Writer writer=new StringWriter();
+ jsonCodec.encode(writer,opackValue);
/*
Decode
*/
-OpackValue decodedOpackValue = jsonCodec.decode(json);
+ OpackValue decodedOpackValue=jsonCodec.decode(json);
```
#### 4. Dense Codec
+
```java
-DenseCodec denseCodec = new DenseCodec.Builder()
+DenseCodec denseCodec=new DenseCodec.Builder()
.setEncodeStackInitialSize(128) // (Optional) Creation size of stack for processing
.setEncodeOutputBufferInitialSize(1024) // (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 = /** See Serialize Usage **/;
+ OpackValue opackValue= /** See Serialize Usage **/;
/*
Encode
*/
-byte[] bytes = denseCodec.encode(opackValue);
+ byte[]bytes=denseCodec.encode(opackValue);
// Or
-OutputStream outputStream = new ByteArrayOutputStream();
-denseCodec.encode(outputStream, opackValue);
+ OutputStream outputStream=new ByteArrayOutputStream();
+ denseCodec.encode(outputStream,opackValue);
/*
Decode
*/
-OpackValue decodedOpackValue1 = denseCodec.decode(bytes);
+ OpackValue decodedOpackValue1=denseCodec.decode(bytes);
// Or
-InputStream inputStream = new ByteArrayInputStream(bytes);
-OpackValue decodedOpackValue2 = denseCodec.decode(inputStream);
+ InputStream inputStream=new ByteArrayInputStream(bytes);
+ OpackValue decodedOpackValue2=denseCodec.decode(inputStream);
```
### Advanced Usage
+
#### 1. Ignore and Type and Name
+
```java
public class SomeObject {
private String stringField;
@@ -172,7 +183,9 @@ public class SomeObject {
private String oldFieldName;
}
```
+
#### 2. Field Transformer
+
```java
public class ByteToBase64Transformer implements Transformer {
@Override
@@ -202,7 +215,9 @@ public class SomeObject {
private byte[] bytesField;
}
```
+
#### 3. Class Transformer
+
```java
public class AnimalTransformer implements Transformer {
/*
@@ -272,44 +287,52 @@ public class SomeObject {
```
#### 4. Handling Opack Value
+
```java
-OpackObject rootObject = new OpackObject<>();
+OpackObject rootObject=new OpackObject<>();
-{
- OpackArray opackArray = new OpackArray<>();
- opackArray.add(Integer.MAX_VALUE);
- rootObject.put("array", opackArray);
-}
+ {
+ 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);
-}
+ {
+ 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 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"
- );
+ 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);
-}
+ rootObject.put("number_map",opackObject);
+ }
-OpackArray opackArray = (OpackArray) rootObject.get("array");
-OpackObject opackObject = (OpackObject) rootObject.get("number_map");
+ 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)));
+ 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`
+
### License
Opack uses [Apache License 2.0](./LICENSE.txt). Please, leave your feedback if you have any suggestions!
diff --git a/src/main/java/com/realtimetech/opack/Opacker.java b/src/main/java/com/realtimetech/opack/Opacker.java
index bbf9183..44feda1 100644
--- a/src/main/java/com/realtimetech/opack/Opacker.java
+++ b/src/main/java/com/realtimetech/opack/Opacker.java
@@ -28,10 +28,17 @@
import com.realtimetech.opack.exception.DeserializeException;
import com.realtimetech.opack.exception.SerializeException;
import com.realtimetech.opack.transformer.Transformer;
+import com.realtimetech.opack.transformer.impl.file.FileTransformer;
import com.realtimetech.opack.transformer.impl.list.ListTransformer;
import com.realtimetech.opack.transformer.impl.list.WrapListTransformer;
import com.realtimetech.opack.transformer.impl.map.MapTransformer;
import com.realtimetech.opack.transformer.impl.map.WrapMapTransformer;
+import com.realtimetech.opack.transformer.impl.path.PathTransformer;
+import com.realtimetech.opack.transformer.impl.time.CalendarTransformer;
+import com.realtimetech.opack.transformer.impl.time.DateTransformer;
+import com.realtimetech.opack.transformer.impl.time.java8.LocalDateTimeTransformer;
+import com.realtimetech.opack.transformer.impl.time.java8.LocalDateTransformer;
+import com.realtimetech.opack.transformer.impl.time.java8.LocalTimeTransformer;
import com.realtimetech.opack.util.OpackArrayConverter;
import com.realtimetech.opack.util.ReflectionUtil;
import com.realtimetech.opack.util.structure.FastStack;
@@ -41,11 +48,14 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
+import java.nio.file.Path;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.*;
public class Opacker {
public static class Builder {
@@ -150,6 +160,16 @@ private Opacker(@NotNull Builder builder) {
} else {
this.typeBaker.registerPredefinedTransformer(Map.class, MapTransformer.class, true);
}
+
+ this.typeBaker.registerPredefinedTransformer(File.class, FileTransformer.class, true);
+ this.typeBaker.registerPredefinedTransformer(Path.class, PathTransformer.class, true);
+
+ this.typeBaker.registerPredefinedTransformer(Date.class, DateTransformer.class, true);
+ this.typeBaker.registerPredefinedTransformer(Calendar.class, CalendarTransformer.class, true);
+
+ this.typeBaker.registerPredefinedTransformer(LocalDate.class, LocalDateTransformer.class, true);
+ this.typeBaker.registerPredefinedTransformer(LocalTime.class, LocalTimeTransformer.class, true);
+ this.typeBaker.registerPredefinedTransformer(LocalDateTime.class, LocalDateTimeTransformer.class, true);
} catch (InstantiationException exception) {
throw new IllegalStateException(exception);
}
@@ -204,7 +224,7 @@ public synchronized OpackValue serialize(@NotNull Object object) throws Serializ
BakedType bakedType = this.typeBaker.get(baseType);
for (Transformer transformer : bakedType.getTransformers()) {
- object = transformer.serialize(this, object);
+ object = transformer.serialize(this, baseType, object);
if (object == null) {
return null;
@@ -317,7 +337,7 @@ private void executeSerializeStack(int endOfStack) throws SerializeException {
Class> fieldType = property.getType();
if (property.getTransformer() != null) {
- element = property.getTransformer().serialize(this, element);
+ element = property.getTransformer().serialize(this, fieldType, element);
}
if (element != null) {
@@ -347,7 +367,8 @@ public synchronized T deserialize(@NotNull Class type, @NotNull OpackValu
throw new DeserializeException("Opacker is serializing.");
int separatorStack = this.objectStack.getSize();
- Object object = this.prepareObjectDeserialize(type, opackValue);
+
+ Object object = this.prepareObjectDeserialize(type, opackValue, null);
if (object == null) {
return null;
@@ -378,12 +399,22 @@ public synchronized T deserialize(@NotNull Class type, @NotNull OpackValu
* @return prepared object
* @throws DeserializeException if a problem occurs during deserializing
*/
- private synchronized @Nullable Object prepareObjectDeserialize(@NotNull Class> goalType, @NotNull Object object) throws DeserializeException {
+ private synchronized @Nullable Object prepareObjectDeserialize(@NotNull Class> goalType, @NotNull Object object, @Nullable Transformer fieldTransformer) throws DeserializeException {
try {
BakedType bakedType = this.typeBaker.get(goalType);
- for (Transformer transformer : bakedType.getTransformers()) {
- object = transformer.deserialize(this, goalType, object);
+ Transformer[] transformers = bakedType.getTransformers();
+
+ for (int index = transformers.length - 1; index >= 0; index--) {
+ object = transformers[index].deserialize(this, goalType, object);
+
+ if (object == null) {
+ return null;
+ }
+ }
+
+ if (fieldTransformer != null) {
+ object = fieldTransformer.deserialize(this, goalType, object);
if (object == null) {
return null;
@@ -465,7 +496,7 @@ public synchronized T deserialize(@NotNull Class type, @NotNull OpackValu
this.typeStack.push(bakedType);
return targetObject;
- } else if (object.getClass() == goalType) {
+ } else if (goalType.isAssignableFrom(object.getClass())) {
return object;
} else {
throw new DeserializeException("Found object, stack corruption.");
@@ -495,7 +526,7 @@ private void executeDeserializeStack(int endOfStack) throws DeserializeException
Object element = opackArray.get(index);
if (element != null) {
- Object deserializedValue = this.prepareObjectDeserialize(componentType, element);
+ Object deserializedValue = this.prepareObjectDeserialize(componentType, element, null);
if (deserializedValue != null) {
ReflectionUtil.setArrayItem(object, index, ReflectionUtil.cast(componentType, deserializedValue));
@@ -514,21 +545,17 @@ private void executeDeserializeStack(int endOfStack) throws DeserializeException
Class> fieldType = property.getType();
Class> actualFieldType = property.getField().getType();
- if (property.getTransformer() != null) {
- element = property.getTransformer().deserialize(this, fieldType, element);
- }
+ Object propertyValue = null;
if (element != null) {
- Object deserializedValue = this.prepareObjectDeserialize(fieldType, element);
+ Object deserializedValue = this.prepareObjectDeserialize(fieldType, element, property.getTransformer());
if (deserializedValue != null) {
- property.set(object, ReflectionUtil.cast(actualFieldType, deserializedValue));
- } else {
- property.set(object, null);
+ propertyValue = ReflectionUtil.cast(actualFieldType, deserializedValue);
}
- } else {
- property.set(object, null);
}
+
+ property.set(object, propertyValue);
} catch (IllegalAccessException | IllegalArgumentException exception) {
throw new DeserializeException("Can't set " + property.getName() + " field in " + bakedType.getType().getSimpleName() + ".", exception);
}
diff --git a/src/main/java/com/realtimetech/opack/bake/TypeBaker.java b/src/main/java/com/realtimetech/opack/bake/TypeBaker.java
index 9fd56e7..f739538 100644
--- a/src/main/java/com/realtimetech/opack/bake/TypeBaker.java
+++ b/src/main/java/com/realtimetech/opack/bake/TypeBaker.java
@@ -23,10 +23,10 @@
package com.realtimetech.opack.bake;
import com.realtimetech.opack.Opacker;
-import com.realtimetech.opack.annotation.Type;
import com.realtimetech.opack.annotation.Ignore;
import com.realtimetech.opack.annotation.Name;
import com.realtimetech.opack.annotation.Transform;
+import com.realtimetech.opack.annotation.Type;
import com.realtimetech.opack.exception.BakeException;
import com.realtimetech.opack.transformer.Transformer;
import com.realtimetech.opack.transformer.TransformerFactory;
@@ -203,7 +203,7 @@ private void addTransformer(@NotNull List<@NotNull Transformer> transformers, @N
if (predefinedTransformers != null) {
for (PredefinedTransformer predefinedTransformer : predefinedTransformers) {
- if (root || predefinedTransformer.isInheritable()) {
+ if (root || predefinedTransformer.isInheritable() && !transformers.contains(predefinedTransformer.getTransformer())) {
transformers.add(predefinedTransformer.getTransformer());
}
}
@@ -233,9 +233,11 @@ private void addTransformer(@NotNull List<@NotNull Transformer> transformers, @N
* @return transformers
* @throws BakeException if transformer class object cannot be instantiated
*/
- private @NotNull Transformer @NotNull[] getTransformer(@NotNull AnnotatedElement annotatedElement) throws BakeException {
+ private @NotNull Transformer @NotNull [] getTransformer(@NotNull AnnotatedElement annotatedElement) throws BakeException {
List transformers = new LinkedList<>();
+
this.addTransformer(transformers, annotatedElement, true);
+
return transformers.toArray(new Transformer[0]);
}
diff --git a/src/main/java/com/realtimetech/opack/codec/OpackCodec.java b/src/main/java/com/realtimetech/opack/codec/OpackCodec.java
index 1f6b8d9..f99f04e 100644
--- a/src/main/java/com/realtimetech/opack/codec/OpackCodec.java
+++ b/src/main/java/com/realtimetech/opack/codec/OpackCodec.java
@@ -28,9 +28,6 @@
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Writer;
public abstract class OpackCodec {
/**
diff --git a/src/main/java/com/realtimetech/opack/transformer/Transformer.java b/src/main/java/com/realtimetech/opack/transformer/Transformer.java
index 5992adb..3ead26a 100644
--- a/src/main/java/com/realtimetech/opack/transformer/Transformer.java
+++ b/src/main/java/com/realtimetech/opack/transformer/Transformer.java
@@ -32,15 +32,18 @@ public interface Transformer {
/**
* Serialize specific value to opack value.
*
- * @param value the value to be serialized
+ * @param opacker the opacker
+ * @param originalType the original type
+ * @param value the value to be serialized
* @return opack value
* @throws SerializeException if a problem occurs during serializing
*/
- @Nullable Object serialize(@NotNull Opacker opacker, @Nullable Object value) throws SerializeException;
+ @Nullable Object serialize(@NotNull Opacker opacker, @NotNull Class> originalType, @Nullable Object value) throws SerializeException;
/**
* Deserialize opack value.
*
+ * @param opacker the opacker
* @param value the opack value to be deserialized
* @param goalType the goal type to deserialize
* @return deserialized value
diff --git a/src/main/java/com/realtimetech/opack/transformer/impl/DataStructureTransformer.java b/src/main/java/com/realtimetech/opack/transformer/impl/DataStructureTransformer.java
index 8a05177..5d77376 100644
--- a/src/main/java/com/realtimetech/opack/transformer/impl/DataStructureTransformer.java
+++ b/src/main/java/com/realtimetech/opack/transformer/impl/DataStructureTransformer.java
@@ -26,7 +26,6 @@
import com.realtimetech.opack.exception.DeserializeException;
import com.realtimetech.opack.exception.SerializeException;
import com.realtimetech.opack.transformer.Transformer;
-import com.realtimetech.opack.value.OpackObject;
import com.realtimetech.opack.value.OpackValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/src/main/java/com/realtimetech/opack/transformer/impl/TypeWrapTransformer.java b/src/main/java/com/realtimetech/opack/transformer/impl/TypeWrapTransformer.java
new file mode 100644
index 0000000..fc3cdc1
--- /dev/null
+++ b/src/main/java/com/realtimetech/opack/transformer/impl/TypeWrapTransformer.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 REALTIMETECH All Rights Reserved
+ *
+ * Licensed either under the Apache License, Version 2.0, or (at your option)
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (subject to the "Classpath" exception),
+ * either version 2, or any later version (collectively, 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
+ * http://www.gnu.org/licenses/
+ * http://www.gnu.org/software/classpath/license.html
+ *
+ * or as provided in the LICENSE file that accompanied this code.
+ * 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.realtimetech.opack.transformer.impl;
+
+import com.realtimetech.opack.Opacker;
+import com.realtimetech.opack.exception.DeserializeException;
+import com.realtimetech.opack.exception.SerializeException;
+import com.realtimetech.opack.transformer.Transformer;
+import com.realtimetech.opack.value.OpackObject;
+import com.realtimetech.opack.value.OpackValue;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class TypeWrapTransformer implements Transformer {
+ /**
+ * Serialize specific value to opack value.
+ *
+ * @param opacker the opacker
+ * @param originalType the original type
+ * @param value the value to be serialized
+ * @return opack value
+ * @throws SerializeException if a problem occurs during serializing
+ */
+ @Override
+ public @Nullable Object serialize(@NotNull Opacker opacker, @NotNull Class> originalType, @Nullable Object value) throws SerializeException {
+ if (value != null) {
+ OpackValue opackValue = opacker.serialize(value);
+ OpackObject