forked from owlike/genson
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix for owlike#123: Initial refactoring based on PR feedback
- Loading branch information
Showing
8 changed files
with
398 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
genson/src/main/java/com/owlike/genson/reflect/Evolvable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.owlike.genson.reflect; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* An interface that can be implemented by data classes | ||
* in order to support schema evolution. | ||
* <p> | ||
* This interface is used in combination with {@link EvolvableHandler} | ||
* in order to prevent data loss during serialization across different | ||
* versions of data classes. | ||
* | ||
* @author Aleksandar Seovic 2018.05.20 | ||
*/ | ||
interface Evolvable { | ||
/** | ||
* Add unknown property to this instance. | ||
* | ||
* @param propName property name | ||
* @param propValue property value | ||
*/ | ||
void addUnknownProperty(String propName, Object propValue); | ||
|
||
/** | ||
* Return a map of unknown properties. | ||
* | ||
* @return a map of unknown properties | ||
*/ | ||
Map<String, Object> unknownProperties(); | ||
} |
64 changes: 64 additions & 0 deletions
64
genson/src/main/java/com/owlike/genson/reflect/EvolvableHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.owlike.genson.reflect; | ||
|
||
import com.owlike.genson.Context; | ||
import com.owlike.genson.GenericType; | ||
import com.owlike.genson.stream.ObjectReader; | ||
import com.owlike.genson.stream.ObjectWriter; | ||
|
||
import java.util.Map; | ||
import java.util.function.Consumer; | ||
|
||
/** | ||
* An implementation of an {@link UnknownPropertyHandler} that supports | ||
* evolution of data classes via {@link Evolvable} interface. | ||
* <p> | ||
* If the target object we are deserializing into is {@link Evolvable}, | ||
* this handler will add any unknown properties encountered during | ||
* deserialization into {@link Evolvable#unknownProperties()} map, | ||
* and will write them out along with all known properties during | ||
* subsequent serialization. | ||
* <p> | ||
* This prevents data loss when serializing and deserializing the same | ||
* JSON payload using different versions of Java data classes. | ||
* | ||
* @author Aleksandar Seovic 2018.05.20 | ||
*/ | ||
public class EvolvableHandler implements UnknownPropertyHandler { | ||
private static final GenericType<Object> UNKNOWN = new GenericType<Object>() {}; | ||
|
||
@Override | ||
public <T> Consumer<T> onUnknownProperty(T target, String propName, ObjectReader reader, Context ctx) { | ||
// TODO: change this to read property as an opaque value, using ObjectReader directly | ||
Object propValue = ctx.genson.deserialize(UNKNOWN, reader, ctx); | ||
|
||
if (target == null) { | ||
// this is a bit ugly... | ||
// the issue is that we may not have a target object while parsing JSON when using creators, | ||
// so we need to store the parsed value somewhere and apply it later | ||
return objTarget -> { | ||
if (objTarget instanceof Evolvable) { | ||
((Evolvable) objTarget).addUnknownProperty(propName, propValue); | ||
} | ||
}; | ||
} | ||
|
||
if (target instanceof Evolvable) { | ||
((Evolvable) target).addUnknownProperty(propName, propValue); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public <T> void writeUnknownProperties(T source, ObjectWriter writer, Context ctx) { | ||
if (source instanceof Evolvable) { | ||
Map<String, Object> props = ((Evolvable) source).unknownProperties(); | ||
if (props != null) { | ||
for (String propName : props.keySet()) { | ||
writer.writeName(propName); | ||
// TODO: change this to write property as an opaque value, using ObjectWriter directly | ||
ctx.genson.serialize(props.get(propName), writer, ctx); | ||
} | ||
} | ||
} | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
genson/src/main/java/com/owlike/genson/reflect/EvolvableObject.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.owlike.genson.reflect; | ||
|
||
import com.owlike.genson.annotation.JsonIgnore; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* Convenience base class for {@link Evolvable} data classes. | ||
* | ||
* @author Aleksandar Seovic 2018.05.20 | ||
*/ | ||
public abstract class EvolvableObject implements Evolvable { | ||
@JsonIgnore | ||
private Map<String, Object> unknownProperties; | ||
|
||
@Override | ||
public void addUnknownProperty(String propName, Object propValue) { | ||
if (unknownProperties == null) { | ||
unknownProperties = new HashMap<>(); | ||
} | ||
unknownProperties.put(propName, propValue); | ||
} | ||
|
||
@Override | ||
public Map<String, Object> unknownProperties() { | ||
return unknownProperties; | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
genson/src/main/java/com/owlike/genson/reflect/UnknownPropertyHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.owlike.genson.reflect; | ||
|
||
import com.owlike.genson.Context; | ||
import com.owlike.genson.stream.ObjectReader; | ||
import com.owlike.genson.stream.ObjectWriter; | ||
|
||
import java.util.function.Consumer; | ||
|
||
/** | ||
* An interface that defines callbacks that will be called when an | ||
* unknown properties are encountered during deserialization, as well | ||
* as to check if there are any unknown properties that should be | ||
* written out during serialization. | ||
* <p> | ||
* The main purpose of this interface is to support schema evolution | ||
* of objects that use JSON as a long term storage format, without | ||
* loss of unknown properties across clients and severs using different | ||
* versions of Java classes. | ||
* | ||
* @author Aleksandar Seovic 2018.05.09 | ||
*/ | ||
public interface UnknownPropertyHandler { | ||
/** | ||
* Called whenever a property is encountered in a JSON document | ||
* that doesn't have a corresponding {@link PropertyMutator}. | ||
* <p> | ||
* Typically, the implementation of this interface concerned | ||
* with schema evolution will handle this event by storing | ||
* property value somewhere so it can be written later by the | ||
* {@link #writeUnknownProperties} method. | ||
* | ||
* @param target the object we are deserializing JSON into, if known | ||
* @param propName the name of the unknown property | ||
* @param reader the ObjectReader to read property value from | ||
* @param ctx deserialization context | ||
* | ||
* @return the optional Consumer that will be called once the target object is known | ||
*/ | ||
<T> Consumer<T> onUnknownProperty(T target, String propName, ObjectReader reader, Context ctx); | ||
|
||
/** | ||
* Write unknown properties encountered during deserialization. | ||
* <p> | ||
* This method can be optionally implemented by {@code UnknownPropertyHandler}s | ||
* that want to write unknown properties during serialization. The default | ||
* implementation is a no-op. | ||
* | ||
* @param source the object we are serializing into JSON | ||
* @param writer the ObjectReader to read property value from | ||
* @param ctx serialization context | ||
* | ||
* @return a map of unknown properties | ||
*/ | ||
default <T> void writeUnknownProperties(T source, ObjectWriter writer, Context ctx) { | ||
} | ||
} |
Oops, something went wrong.