package ai.cometandroid.network; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.internal.LinkedHashTreeMap; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import ai.cometandroid.models.JsonApiResponse; import retrofit.converter.ConversionException; import retrofit.converter.Converter; import retrofit.mime.TypedInput; import retrofit.mime.TypedOutput; import rx.Observable; /** * Created by greg on 6/26/15. */ public class JsonApiConverter implements Converter { private static String RELATIONSHIP_KEY = "relationships"; private static String ATTRIBUTES_KEY = "attributes"; @Override public Object fromBody(TypedInput body, Type type) throws ConversionException { try { InputStream in = body.in(); String json = fromJsonApi(fromStream(in)); if(String.class.equals(type)) return json; else return new Gson().fromJson(json, type); } catch (Exception e) { throw new ConversionException(e); } } @Override public TypedOutput toBody(Object object) { try { Map<String, Object> jsonApi = new LinkedHashTreeMap<>(); jsonApi.put("data", object); return new JsonTypedOutput(new Gson().toJson(jsonApi).getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } } private static class JsonTypedOutput implements TypedOutput { private final byte[] jsonBytes; JsonTypedOutput(byte[] jsonBytes) { this.jsonBytes = jsonBytes; } @Override public String fileName() { return null; } @Override public String mimeType() { return "application/json; charset=UTF-8"; } @Override public long length() { return jsonBytes.length; } @Override public void writeTo(OutputStream out) throws IOException { out.write(jsonBytes); } } private static String fromStream(InputStream in) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuilder out = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { out.append(line); out.append("\r\n"); } return out.toString(); } private static String fromJsonApi(String json) throws Exception { Gson gson = new Gson(); JsonApiResponse response = gson.fromJson(json, JsonApiResponse.class); List<Map<String, Object>> data = new ArrayList<>(); response.data() .doOnNext(stringObjectMap -> { stringObjectMap.putAll((Map<String, Object>) stringObjectMap.get(ATTRIBUTES_KEY)); stringObjectMap.remove(ATTRIBUTES_KEY); Observable.from(((Map<String, Object>) stringObjectMap.get(RELATIONSHIP_KEY)).keySet()) .filter(key -> ((Map<String, Object>) stringObjectMap.get(RELATIONSHIP_KEY)).get(key) instanceof Map || ((Map<String, Object>) stringObjectMap.get(RELATIONSHIP_KEY)).get(key) instanceof List) .subscribe(key -> { stringObjectMap.put(key, ((Map<String, Object>) ((Map<String, Object>) stringObjectMap.get(RELATIONSHIP_KEY)).get(key)).get("data")); Observable<Map<String, Object>> inner; Object includedLinks = stringObjectMap.get(key); if (includedLinks instanceof List) inner = Observable.from((List<Map<String, Object>>) includedLinks); else inner = Observable.just((Map<String, Object>) includedLinks); inner.forEach(link -> response.included() .filter(included -> included.get("type").equals(link.get("type")) && included.get("id").equals(link.get("id"))) .first() .subscribe(included -> link.putAll((Map<String, Object>) included.get(ATTRIBUTES_KEY)))); }); stringObjectMap.remove(RELATIONSHIP_KEY); }) .subscribe(datum -> data.add(datum)); String formatted; if(data.size() == 1) { formatted = gson.toJson(data.get(0)); }else { formatted = gson.toJson(data); } return formatted; } }