package org.edx.mobile.model.course; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; import org.edx.mobile.logger.Logger; import java.lang.reflect.Type; import java.util.Locale; import java.util.Map; /** * Created by hanning on 5/19/15. */ public class CourseStructureJsonHandler { protected static final Logger logger = new Logger(CourseStructureJsonHandler.class.getName()); public CourseStructureV1Model processInput(String jsonInput) { BlockDataDeserializer dataDeserializer = new BlockDataDeserializer(); BlockListDeserializer blockListDeserializer = new BlockListDeserializer(); BlockTypeDeserializer blockTypeListDeserializer = new BlockTypeDeserializer(); //ideally we should use this approach. but it requires base class to //define some properties. // RuntimeTypeAdapterFactory<BlockData> rta = RuntimeTypeAdapterFactory.of( // BlockData.class) // .registerSubtype(VideoData.class); Gson gson = new GsonBuilder() //.registerTypeAdapterFactory(rta) .registerTypeAdapter(BlockData.class, dataDeserializer) .registerTypeAdapter(BlockType.class, blockTypeListDeserializer) .registerTypeAdapter(BlockList.class, blockListDeserializer).create(); return gson.fromJson(jsonInput, CourseStructureV1Model.class); } private static class BlockDataDeserializer implements JsonDeserializer<BlockData> { @Override public BlockData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); //TODO - can not figure out a way to pass parent properties, for example, "type" field //so have to check the existence of certain fields if (jsonObject.has("encoded_videos") || jsonObject.has("transcripts")) { return context.deserialize(jsonObject, VideoData.class); } else if (jsonObject.has("topic_id")) { return context.deserialize(jsonObject, DiscussionData.class); } return new BlockData(); } } private static class BlockListDeserializer implements JsonDeserializer<BlockList> { @Override public BlockList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Type mapType = new TypeToken<Map<String, BlockModel>>() {}.getType(); JsonObject jsonObject = json.getAsJsonObject(); Map<String, BlockModel> map = (Map<String, BlockModel>) context.deserialize(jsonObject, mapType); return new BlockList(map); } } private static class BlockTypeDeserializer implements JsonDeserializer<BlockType> { @Override public BlockType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { String value = json.getAsString(); try { //we force the String.toUpperCase to use English local. //as it is just a mapping from english string to constants. return BlockType.valueOf(value.toUpperCase(Locale.US)); } catch (Exception ex) { logger.debug(ex.getMessage()); return BlockType.OTHERS; } } } }