フラットに属性が並んだオブジェクトから、階層のあるJSON への変換(1)の続き。
属性フィールドに、グルーピング名をアノテーションで付与することで、この処理を汎用的に行う
シリアライザを考えた。
public class Sample{ public String title; @Grouping("Unit") public String name; @Grouping("Unit") public int value; }
これで、 name と value を、以下のJSONのように、グループする。
{ "title": "あいう", "Unit": { "name": "A", "value": 121 } }
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Grouping */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD}) public @interface Grouping { String value(); boolean nullSafe() default true; }
Grouping アノテーションが付いたフィールドを処理するシリアライザ
import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.Optional; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.annotations.SerializedName; /** * GroupingSerializer */ public class GroupingSerializer<T> implements JsonSerializer<T>{ @Override public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context){ JsonObject jo = new JsonObject(); Field[] fields = src.getClass().getDeclaredFields(); Map<String, JsonObject> jmap = new HashMap<>(); for(Field f:fields) { try{ f.setAccessible(true); Object obj = f.get(src); JsonObject addjo; Grouping g = f.getAnnotation(Grouping.class); if (g != null) { String jkey = g.value(); addjo = jmap.containsKey(jkey) ? jmap.get(jkey) : new JsonObject(); jmap.put(jkey, addjo); }else{ addjo = jo; } if (obj instanceof String) { addjo.addProperty(Optional.ofNullable(f.getAnnotation(SerializedName.class)).map(e->e.value()).orElse(f.getName()) , Optional.ofNullable(obj).map(e->e.toString()).orElse(null)); }else { addjo.add(Optional.ofNullable(f.getAnnotation(SerializedName.class)).map(e->e.value()).orElse(f.getName()) , context.serialize(obj, f.getType())); } }catch(IllegalAccessException e){ throw new RuntimeException(e); } } jmap.entrySet().stream().forEach(e->jo.add(e.getKey(), e.getValue())); return jo; } }
@SerializedName も働くようにしてある。