I would like to marshall and unmarshall objects whose fields are of their class's inner class (synthetic class if I'm not wrong).
class A {
private B field_b=null;
public static class B {
public static B B1 = new B("b1");
public static B B2 = new B("b2");
private final String name;
private B(String name) {
this.name=name;
}
}
public B getBforName(String name) {
if (B1.name.equals(name) return B1;
else if (B2.name.equals(name) return B2;
else return null;
}
And produce and read from an XML:
<A>
<field_B>b1</field_B>
</A>
The writing part is easy.
The reading part is more complicated.
I would like to write a converter:
public class BConverter implements Converter {
public boolean canConvert(Class type) {
return B.class.isAssignableFrom(type) ;
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
// ...
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
A parent_v1 = (A) context.getCurrentObject(); // !!! always empty
A parent_v2 = (A) context.get("current_unmarshalled_A");
return parent_v2.getBforName((String)reader.getValue());
}
}
The context.getCurrentObject() returns null and seems to be obsolete (from forums I read).
The context.get("current_unmarshalled_A")would require that I put in this unMarshallingContext that key and the A object being unmarshalled. I don't find to do that without writing an AConverter. And that is not neat as I would loose the default unmarshalling behaviour for the class A.
Anyone has an idea ?
I'm not sure this is cleanest way to that but it works.
I use a static method in the B class ...
class A {
private B field_b=null;
public static class B {
public static B B1 = new B("b1");
public static B B2 = new B("b2");
public static B getForName(String name) {
if (B1.name.equals(name) return B1;
else if (B2.name.equals(name) return B2;
else return null;
}
private final String name;
private B(String name) {
this.name=name;
}
}
... and reflection in the Converter
public class BConverter implements Converter {
public boolean canConvert(Class type) {
return B.class.isAssignableFrom(type) ;
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
writer.setValue(((B)source).getName())
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
try {
Method method = context.getRequiredType().getMethod("getForName", String.class);
final String v = reader.getValue();
Object b= method.invoke(null, v);
if (b== null)
throw new ConversionException("Could not retrieve a B object for \"" + v + "\"");
return b;
} catch (Exception ex) {
throw new ConversionException("Error while retrieving a B object", ex);
}
}
}
Problem:
I am deserializing enums with Jackson that don't match up with their name in the code, below is a sample of json.
{
"thing1": {"foo": "cool-guy"},
"thing2": {"foo": "loser-face"}
}
Here is the enum, I will explain the interface later.
enum Foo implements HasText {
COOL_GUY("cool-guy"), LOSER_FACE("loser-face"), // etc...
private String text;
private Foo(String text) {
this.text = text;
}
#Override
public String getText() {
return text;
}
}
I know how to solve this issue for each enum individually by making a deserializer (below) and the annotation #JsonDeserialize(using = FooDeserializer .class) on the setter method for foo.
public class FooDeserializer extends JsonDeserializer<Enum<Foo>> {
#Override
public Foo deserialize(JsonParser p, DeserializationContext context) throws Exception {
if (p.getCurrentToken().equals(JsonToken.VALUE_STRING)) {
String jsonText = p.getText();
Stream<Foo> stream = Arrays.asList(Foo.values()).stream();
return stream.filter(a -> a.getText().equals(jsonText.toLowerCase())).findAny().get();
}
throw context.mappingException(Foo.class);
}
}
Question:
Is there a way to do this abstractly? That's why I added the HasText interface to all my enums in hopes there was a way to do something like this:
public class EnumWithTextDeserializer<T extends Enum<T> & HasText> extends JsonDeserializer<T> {
#Override
public T deserialize(JsonParser p, DeserializationContext context) throws Exception {
if (p.getCurrentToken().equals(JsonToken.VALUE_STRING)) {
final String jsonText = p.getText();
final Stream<T> stream = Arrays.asList(runtimeClass().getEnumConstants()).stream();
return stream.filter(a -> a.getText().equals(jsonText.toLowerCase())).findAny().get();
}
throw context.mappingException(runtimeClass());
}
#SuppressWarnings("unchecked")
private Class<T> runtimeClass() {
ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
return (Class<T>) superclass.getActualTypeArguments()[0];
}
}
The compile won't let me annotate the setter method (#JsonDeserialize(using = EnumWithTextDeserializer.class)) with this class though because
Type mismatch: cannot convert from Class<EnumWithTextDeserializer> to Class<? extends JsonDeserializer<?>>".
Really, all I want to be able to do is deserialize these enums based on the getText() method.
In order to deserialize, you can specify your String value using #JsonValue.
public enum FooEnum implements WithText {
AWESOME("awesome-rad"),
NARLY("totally-narly");
private final String text;
FooEnum(String text) {
this.text = text;
}
#Override
#JsonValue
public String getText() {
return text;
}
}
Then executing this code to serialize/deserialize
ImmutableMap<String, FooEnum> map = ImmutableMap.of("value", FooEnum.AWESOME, "value2", FooEnum.NARLY);
final String value;
try {
value = objectMapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
throw Throwables.propagate(e);
}
Map<String, FooEnum> read;
try {
read = objectMapper.readValue(value, new TypeReference<Map<String, FooEnum>>() {});
} catch (IOException e) {
throw Throwables.propagate(e);
}
I get:
read = {LinkedHashMap#4627} size = 2
0 = {LinkedHashMap$Entry#4631} "value1" -> "AWESEOME"
1 = {LinkedHashMap$Entry#4632} "value2" -> "NARLY"
I have the following code:
public static class A
{
public A() {}
private List<B> bs = new ArrayList<B>();
public List<B> getBs() {
return bs;
}
public void setBs(List<B> bs) {
this.bs = bs;
}
}
public static class B
{
B(String foo){this.foo=foo;}
private String foo;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
public static void main(String[] args) throws Exception {
Gson gson = new Gson();
A a = new A();
a.getBs().add(new B("bar"));
System.out.println(gson.toJson(a));
}
and as expected the output is:
{"bs":[{"foo":"bar"}]}
However, if I make A a subclass of HashMap:
public static class A extends HashMap
I get an empty set returned: {}
I have even tried:
System.out.println(gson.toJson(a, new TypeToken<A>(){}.getType()));
and:
System.out.println(gson.toJson(a, new TypeToken<HashMap>(){}.getType()));
Can someone tell me whether/how I can serialise this HashMap subclass using GSON?
Gson works with (default and custom) TypeAdapterFactory instances and the TypeAdapter objects they create to serialize/deserialize your objects.
It goes through the list of registered TypeAdapterFactory objects and picks the first one that can create an appropriate TypeAdapter for the type of the object your are providing. One of these TypeAdapterFactory objects, is one of type MapTypeAdapterFactory which creates a TypeAdapter (of type MapTypeAdapterFactory$Adapter) that serializes/deserializes based on the java.util.Map interface (keys/values). It does nothing about your custom sub type's fields.
If you want Gson to serialize your type as both a Map and a custom type, you will need to register either a custom TypeAdapter directly or a custom TypeAdapterFactory that creates TypeAdapter objects.
Here is the custom TypeAdapterFactory.
Test:
public static void main(String[] args) throws Exception{
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new RetainFieldMapFactory())
.create();
Foo f = gson.fromJson("{'key1':'value1','key2':'value2'}", Foo.class);
System.out.println("in map:\t" + f.toString());
System.out.println("f.key1:\t"+f.key1);
System.out.println("toJson:\t"+gson.toJson(f));
}
public static class Foo extends HashMap<String, String> {
private String key1;
}
Output:
in map: {key2=value2}
f.key1: value1
toJson: {"key2":"value2","key1":"value1"}
RetainFieldMapFactory.java:
/**
* Created by linfaxin on 2015/4/9 009.
* Email: linlinfaxin#163.com
*/
public class RetainFieldMapFactory implements TypeAdapterFactory {
FieldNamingPolicy fieldNamingPolicy = FieldNamingPolicy.IDENTITY;
ConstructorConstructor constructorConstructor = new ConstructorConstructor(Collections.<Type, InstanceCreator<?>>emptyMap());
MapTypeAdapterFactory defaultMapFactory = new MapTypeAdapterFactory(constructorConstructor, false);
ReflectiveFilterMapFieldFactory defaultObjectFactory = new ReflectiveFilterMapFieldFactory(constructorConstructor,
fieldNamingPolicy, Excluder.DEFAULT);
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> mapAdapter = defaultMapFactory.create(gson, type);
if(mapAdapter!=null){
return (TypeAdapter<T>) new RetainFieldMapAdapter(mapAdapter, defaultObjectFactory.create(gson, type));
}
return mapAdapter;
}
class RetainFieldMapAdapter extends TypeAdapter<Map<String, Object>>{
TypeAdapter<Map<String, Object>> mapAdapter;
ReflectiveTypeAdapterFactory.Adapter<Map<String, Object>> objectAdapter;
RetainFieldMapAdapter(TypeAdapter mapAdapter, ReflectiveTypeAdapterFactory.Adapter objectAdapter) {
this.mapAdapter = mapAdapter;
this.objectAdapter = objectAdapter;
}
#Override
public void write(final JsonWriter out, Map<String, Object> value) throws IOException {
//1.write object
StringWriter sw = new StringWriter();
objectAdapter.write(new JsonWriter(sw), value);
//2.convert object to a map
Map<String, Object> objectMap = mapAdapter.fromJson(sw.toString());
//3.overwrite fields in object to a copy map
value = new LinkedHashMap<String, Object>(value);
value.putAll(objectMap);
//4.write the copy map
mapAdapter.write(out, value);
}
#Override
public Map<String, Object> read(JsonReader in) throws IOException {
//1.create map, all key-value retain in map
Map<String, Object> map = mapAdapter.read(in);
//2.create object from created map
Map<String, Object> object = objectAdapter.fromJsonTree(mapAdapter.toJsonTree(map));
//3.remove fields in object from map
for(String field : objectAdapter.boundFields.keySet()){
map.remove(field);
}
//4.put map to object
object.putAll(map);
return object;
}
}
/**
* If class is extends from some custom map,
* class should implement this to avoid serialize custom map's fields
*/
public interface RetainFieldFlag {}
static class ReflectiveFilterMapFieldFactory extends ReflectiveTypeAdapterFactory{
public ReflectiveFilterMapFieldFactory(ConstructorConstructor constructorConstructor, FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
super(constructorConstructor, fieldNamingPolicy, excluder);
}
#Override
protected boolean shouldFindFieldInClass(Class willFindClass, Class<?> originalRaw) {
if(RetainFieldFlag.class.isAssignableFrom(originalRaw)){
return RetainFieldFlag.class.isAssignableFrom(willFindClass);
}else{
Class[] endClasses = new Class[]{Object.class, HashMap.class, LinkedHashMap.class,
LinkedTreeMap.class, Hashtable.class, TreeMap.class, ConcurrentHashMap.class,
IdentityHashMap.class, WeakHashMap.class, EnumMap.class};
for(Class c : endClasses){
if(willFindClass == c) return false;
}
}
return super.shouldFindFieldInClass(willFindClass, originalRaw);
}
}
/**
* below code copy from {#link com.google.gson.internal.bind.ReflectiveTypeAdapterFactory}
* (little modify, in source this class is final)
* Type adapter that reflects over the fields and methods of a class.
*/
static class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
private final FieldNamingStrategy fieldNamingPolicy;
private final Excluder excluder;
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
this.constructorConstructor = constructorConstructor;
this.fieldNamingPolicy = fieldNamingPolicy;
this.excluder = excluder;
}
public boolean excludeField(Field f, boolean serialize) {
return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
}
private String getFieldName(Field f) {
SerializedName serializedName = f.getAnnotation(SerializedName.class);
return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
}
public <T> Adapter<T> create(Gson gson, final TypeToken<T> type) {
Class<? super T> raw = type.getRawType();
if (!Object.class.isAssignableFrom(raw)) {
return null; // it's a primitive!
}
ObjectConstructor<T> constructor = constructorConstructor.get(type);
return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
}
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
final Gson context, final Field field, final String name,
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
// special casing primitives here saves ~5% on Android...
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType);
#SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
#Override void write(JsonWriter writer, Object value)
throws IOException, IllegalAccessException {
Object fieldValue = field.get(value);
TypeAdapter t = new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
t.write(writer, fieldValue);
}
#Override void read(JsonReader reader, Object value)
throws IOException, IllegalAccessException {
Object fieldValue = typeAdapter.read(reader);
if (fieldValue != null || !isPrimitive) {
field.set(value, fieldValue);
}
}
};
}
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
if (raw.isInterface()) {
return result;
}
Type declaredType = type.getType();
Class<?> originalRaw = type.getRawType();
while (shouldFindFieldInClass(raw, originalRaw)) {
Field[] fields = raw.getDeclaredFields();
for (Field field : fields) {
boolean serialize = excludeField(field, true);
boolean deserialize = excludeField(field, false);
if (!serialize && !deserialize) {
continue;
}
field.setAccessible(true);
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
BoundField boundField = createBoundField(context, field, getFieldName(field),
TypeToken.get(fieldType), serialize, deserialize);
BoundField previous = result.put(boundField.name, boundField);
if (previous != null) {
throw new IllegalArgumentException(declaredType
+ " declares multiple JSON fields named " + previous.name);
}
}
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
raw = type.getRawType();
}
return result;
}
protected boolean shouldFindFieldInClass(Class willFindClass, Class<?> originalRaw){
return willFindClass != Object.class;
}
static abstract class BoundField {
final String name;
final boolean serialized;
final boolean deserialized;
protected BoundField(String name, boolean serialized, boolean deserialized) {
this.name = name;
this.serialized = serialized;
this.deserialized = deserialized;
}
abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
}
public static final class Adapter<T> extends TypeAdapter<T> {
private final ObjectConstructor<T> constructor;
private final Map<String, BoundField> boundFields;
private Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
this.constructor = constructor;
this.boundFields = boundFields;
}
#Override public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
T instance = constructor.construct();
try {
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
BoundField field = boundFields.get(name);
if (field == null || !field.deserialized) {
in.skipValue();
} else {
field.read(in, instance);
}
}
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
in.endObject();
return instance;
}
#Override public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
try {
for (BoundField boundField : boundFields.values()) {
if (boundField.serialized) {
out.name(boundField.name);
boundField.write(out, value);
}
}
} catch (IllegalAccessException e) {
throw new AssertionError();
}
out.endObject();
}
}
}
static class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
private final Gson context;
private final TypeAdapter<T> delegate;
private final Type type;
TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
this.context = context;
this.delegate = delegate;
this.type = type;
}
#Override
public T read(JsonReader in) throws IOException {
return delegate.read(in);
}
#SuppressWarnings({"rawtypes", "unchecked"})
#Override
public void write(JsonWriter out, T value) throws IOException {
// Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
// Fourth preference: reflective type adapter for the declared type
TypeAdapter chosen = delegate;
Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
if (runtimeType != type) {
TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for the runtime type, so we will use that
chosen = runtimeTypeAdapter;
} else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for Base class, so we prefer it over the
// reflective type adapter for the runtime type
chosen = delegate;
} else {
// Use the type adapter for runtime type
chosen = runtimeTypeAdapter;
}
}
chosen.write(out, value);
}
/**
* Finds a compatible runtime type if it is more specific
*/
private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
if (value != null
&& (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
type = value.getClass();
}
return type;
}
}
}
In my GWT web application I have a textbox that holds a price.
How can one convert that String to a BigDecimal?
The easiest way is to create new text box widget that inherits ValueBox.
If you do it this way, you won't have to convert any string values manually. the ValueBox takes care of it all.
To get the BigDecimal value entered you can just go:
BigDecimal value = myTextBox.getValue();
Your BigDecimalBox.java:
public class BigDecimalBox extends ValueBox<BigDecimal> {
public BigDecimalBox() {
super(Document.get().createTextInputElement(), BigDecimalRenderer.instance(),
BigDecimalParser.instance());
}
}
Then your BigDecimalRenderer.java
public class BigDecimalRenderer extends AbstractRenderer<BigDecimal> {
private static BigDecimalRenderer INSTANCE;
public static Renderer<BigDecimal> instance() {
if (INSTANCE == null) {
INSTANCE = new BigDecimalRenderer();
}
return INSTANCE;
}
protected BigDecimalRenderer() {
}
public String render(BigDecimal object) {
if (null == object) {
return "";
}
return NumberFormat.getDecimalFormat().format(object);
}
}
And your BigDecimalParser.java
package com.google.gwt.text.client;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.text.shared.Parser;
import java.text.ParseException;
public class BigDecimalParser implements Parser<BigDecimal> {
private static BigDecimalParser INSTANCE;
public static Parser<BigDecimal> instance() {
if (INSTANCE == null) {
INSTANCE = new BigDecimalParser();
}
return INSTANCE;
}
protected BigDecimalParser() {
}
public BigDecimal parse(CharSequence object) throws ParseException {
if ("".equals(object.toString())) {
return null;
}
try {
return new BigDecimal(object.toString());
} catch (NumberFormatException e) {
throw new ParseException(e.getMessage(), 0);
}
}
}
Take a look at GWT-Math.
I have a lot of classes UNO,HAV,MAS,KOS
I want to create a factory pattern.
validator.load("UNO").validate();
I need dynamically load classes into validator class and return an instance.
(dynamically set name of the class and return an instance)
My problem is: how can I return the instance of a class, if I have incompatible types?
I don't know what to write in return type of method.
The main problem in the Validator CLASS.
public SegmentAbstract load(String str) {
AND
return SegmentAbsClass.forName(identify);
Main class
try{
validator.load("UNO").validate();
}catch(Exception e){
System.out.print("No class ");
}
Abstract Class (SegmentAbstract)
public abstract class SegmentAbstract {
public abstract Boolean validate();
}
Class UNO
public class UNA extends SegmentAbstract{
public Boolean validate() {
System.out.print("UNO!!");
return true;
}
}
Class Validator
public class Validator {
public SegmentAbstract load(String str) {
String identify = str.substring(0, 3);
try {
return SegmentAbsClass.forName(identify);
}
catch(Exception e) {
return this;
}
}
}
Try this :
public interface Validator {
boolean validate(Object obj);
}
public final class ValidatorFactory {
private ValidatorFactory(){}
public static Validator load(String type){
try {
Class<?> clazz = Class.forName(type);
if (Arrays.asList(clazz.getInterfaces()).contains(Validator.class)){
return (Validator) clazz.newInstance();
}
throw new IllegalArgumentException("Provided class doesn't implement Validator interface");
} catch (Exception e) {
throw new IllegalArgumentException("Wrong class provided", e);
}
}
}
Maybe this will help???
I will do something like that:
// ISegment.java
public interface ISegment {
Boolean validate();
}
// Uno.java
public class Uno implements ISegment {
public Boolean validate() {
System.out.print("UNO!!");
return true;
}
}
// SegmentFactory.java
public final class SegmentFactory {
public static enum Supported {
UNO("uno", Uno.class), /* ... */, HAV("hav", Hav.class);
private final Class<?> clazz;
private final String name;
private Supported(final String name, final Class<?> clazz) {
this.name = name;
this.clazz = clazz;
}
public Class<?> getClazz() {
return clazz;
}
public static Supported for(final String name) {
for (final Supported s : values()) {
if (s.name.equals(name) {
return s;
}
}
return null; // a default one
}
}
public static ISegment create(final Supported supp) {
if (supp == null) {
return null;
}
return supp.getClazz.newInstance();
}
private SegmentFactory() {
// avoid instantiation
}
}
usage:
final ISegment sa = SegmentFactory.create(SegmentFactory.Supported.for("uno"));
sa.validate();
Not tested!!
Take a look here. Briefly, the idea is to create a map in your factory class (Map<String,String>, key is identifier, value is fully qualified class name), and add supported classes during initialization. Then you use reflection to instantiate an object in your factory method. Also, you can avoid reflection by using Map<String, SegmentAbstract> instead of Map<String,String> and adding public abstract getNewSegment() to your SegmentAbstract class.