0

It might be too easy but I just cannot find a solution to this. How can I parse the array, in which there are arrays of objects? This is my example Json

{  
   "status":"Ok",
   "items":[  
      [  
         "1530871200000",
         {  
            "o":"24174.11",
            "c":"24320.92",
            "h":"24350",
            "l":"24170.08",
            "v":"61.75980004"
         }
      ],
      [  
         "1530878400000",
         {  
            "o":"24322.74",
            "c":"24308.52",
            "h":"24632.36",
            "l":"24300",
            "v":"98.16061555"
         }
      ],
      [  
         "1530885600000",
         {  
            "o":"24367.16",
            "c":"24450",
            "h":"24450",
            "l":"24290.13",
            "v":"56.9420044"
         }
      ],
      [  
         "1530892800000",
         {  
            "o":"24427.73",
            "c":"24413.87",
            "h":"24542.69",
            "l":"24356.91",
            "v":"81.64183111"
         }
      ],
      [  
         "1530900000000",
         {  
            "o":"24472.81",
            "c":"24275",
            "h":"24538",
            "l":"24215.72",
            "v":"75.40993175"
         }
      ],
      [  
         "1530907200000",
         {  
            "o":"24275",
            "c":"24420",
            "h":"24461.61",
            "l":"24249.42",
            "v":"53.58580779"
         }
      ],
      [  
         "1530914400000",
         {  
            "o":"24420",
            "c":"24437",
            "h":"24538.11",
            "l":"24293.91",
            "v":"53.87857788"
         }
      ],
      [  
         "1530921600000",
         {  
            "o":"24437",
            "c":"24413.82",
            "h":"24449.11",
            "l":"24411.13",
            "v":"0.48062667"
         }
      ],
      [  
         "1530928800000",
         {  
            "o":"24414.05",
            "c":"24494.37",
            "h":"24505.7",
            "l":"24293.9",
            "v":"15.75597079"
         }
      ],
      [  
         "1530936000000",
         {  
            "o":"24500",
            "c":"24486.86",
            "h":"24500",
            "l":"24459.25",
            "v":"2.34557367"
         }
      ],
      [  
         "1530943200000",
         {  
            "o":"24486.86",
            "c":"24482",
            "h":"24531.43",
            "l":"24434.04",
            "v":"60.734314"
         }
      ],
      [  
         "1530950400000",
         {  
            "o":"24549.38",
            "c":"24444",
            "h":"24583",
            "l":"24406.13",
            "v":"56.25116508"
         }
      ]
   ]
}

"1530871200000" this value is random. Previously, when I had the Json to parse with an object {} I just used the POJO class + gson. Now I can't cope with parsing the array, in which there are arrays of objects. Lower I paste the Pojo that I wanted to control with this Json.

Chart.java

private void Chart(String cryptoCurrency, String fiatcurrency, int resolution, long from)
{
        BitBayInterface3 charts = BitBayInterface3.retrofitAPI3.create(BitBayInterface3.class);
        Call <Chart> call = charts.chartApi3(cryptoCurrency, fiatcurrency, resolution, from, System.currentTimeMillis());
        call.enqueue(new Callback <Chart>() {
            @Override
            public void onResponse(Call <Chart> call, Response <Chart> response)
            {
                List <ItemClass> itemClasses = new ArrayList<>();
                for (Map.Entry<String, ItemClass> entry : response.body().getItems().entrySet()) {
                    itemClasses.add(new ItemClass(entry.getKey(), entry.getValue().getO(), entry.getValue().getC(), entry.getValue().getH(), entry.getValue().getL(), entry.getValue().getV()));
                }
                Log.e(TAG, "CHART SERVICE response isSuccessful " + " body " + itemClasses.size());
            }
            @Override
            public void onFailure(@NonNull Call <Chart> call, Throwable t) {
                Log.e(TAG, "CHART SERVICE " + t);
            }
        });
}

ItemClass.java

public class ItemClass {
private String o;
private String c;
private String h;
private String l;
private String v;
private String timestamp;

public ItemClass(String timestamp, String o, String c, String h, String l, String v)
{
    this.timestamp = timestamp;
    this.o = o;
    this.c = c;
    this.h = h;
    this.l = l;
    this.v = v;
}

@JsonProperty("o")
public String getO() { return o; }
@JsonProperty("o")
public void setO(String value) { this.o = value; }

@JsonProperty("c")
public String getC() { return c; }
@JsonProperty("c")
public void setC(String value) { this.c = value; }

@JsonProperty("h")
public String getH() { return h; }
@JsonProperty("h")
public void setH(String value) { this.h = value; }

@JsonProperty("l")
public String getL() { return l; }
@JsonProperty("l")
public void setL(String value) { this.l = value; }

@JsonProperty("v")
public String getV() { return v; }
@JsonProperty("v")
public void setV(String value) { this.v = value; }

public String getTimestamp() {
    return timestamp;
}

public void setTimestamp(String timestamp) {
    this.timestamp = timestamp;
}
}

ItemElement.java

public class ItemElement {
public ItemClass itemClassValue;
public String stringValue;

static class Deserializer extends JsonDeserializer<ItemElement> {
    @Override
    public ItemElement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        ItemElement value = new ItemElement();
        switch (jsonParser.getCurrentToken()) {
            case VALUE_STRING:
                value.stringValue = jsonParser.readValueAs(String.class);
                break;
            case START_OBJECT:
                value.itemClassValue = jsonParser.readValueAs(ItemClass.class);
                break;
            default: throw new IOException("Cannot deserialize ItemElement");
        }
        return value;
    }
}

static class Serializer extends JsonSerializer<ItemElement> {
    @Override
    public void serialize(ItemElement obj, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (obj.itemClassValue != null) {
            jsonGenerator.writeObject(obj.itemClassValue);
            return;
        }
        if (obj.stringValue != null) {
            jsonGenerator.writeObject(obj.stringValue);
            return;
        }
        throw new IOException("ItemElement must not be null");
    }
}
}

ChartConverter.java

public class ChartConverter {
// Serialize/deserialize helpers

public static Chart fromJsonString(String json) throws IOException {
    return getObjectReader().readValue(json);
}

public static String toJsonString(Chart obj) throws JsonProcessingException {
    return getObjectWriter().writeValueAsString(obj);
}

private static ObjectReader reader;
private static ObjectWriter writer;

private static void instantiateMapper() {
    ObjectMapper mapper = new ObjectMapper();
    reader = mapper.reader(Chart.class);
    writer = mapper.writerFor(Chart.class);
}

private static ObjectReader getObjectReader() {
    if (reader == null) instantiateMapper();
    return reader;
}

private static ObjectWriter getObjectWriter() {
    if (writer == null) instantiateMapper();
    return writer;
}
}

retrofit2 void

private void Chart(String cryptoCurrency, String fiatcurrency, int resolution, long from)
{
        BitBayInterface3 charts = BitBayInterface3.retrofitAPI3.create(BitBayInterface3.class);
        Call <Chart> call = charts.chartApi3(cryptoCurrency, fiatcurrency, resolution, from, System.currentTimeMillis());
        call.enqueue(new Callback <Chart>() {
            @Override
            public void onResponse(Call <Chart> call, Response <Chart> response)
            {
                try {
                    Chart data = ChartConverter.fromJsonString(new Gson().toJson(response.body().getItems()));
                    Log.e(TAG, "CHART SERVICE response isSuccessful " + " body " + data.getItems().length);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onFailure(@NonNull Call <Chart> call, Throwable t) {
                Log.e(TAG, "CHART SERVICE " + t);
            }
        });
}

and Interface

public interface BitBayInterface3 {

String BASE_URL_API3 = "https://api.bitbay.net/rest/";

Retrofit retrofitAPI3 = new Retrofit.Builder()
        .baseUrl(BASE_URL_API3)
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .build();


@GET("trading/candle/history/{crypto}-{fiat}/{resolution}")
Call <Chart> chartApi3(
        @Path("crypto") String crypto,
        @Path("fiat") String fiat,
        @Path("resolution") int resolution,
        @Query("from") long from,
        @Query("to") long to
);}

And this rejects the following error "com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 27 path $.items[0][0]"

I need an object in the form of chart.get(0).getObjectKey(); or chart.get(0).getC();

The only method that does not work entirely is to replace ItemElement[][] with Map, but then I cannot get to a single element or to the key of an object that is generated randomly.

@EDIT 13:19

It now works without ItemElement or ChartConverter. I don't know if this is the best solution, but until I find a better one, I will apply it.

Community
  • 1
  • 1
Mich
  • 71
  • 13

1 Answers1

3

items is an array of array of either String or ItemClass, so its type must be one of the following, since Object is the only common type of String and ItemClass:

  • Object[][]
  • List<List<Object>>
  • Other combination of above

Opinion: It's a badly designed JSON. If you could change it, you should do one of the following:

  • Change inner array to object:

    "items":[  
          {  
             "a":"1530871200000",
             "b":{  
                "o":"24174.11",
                "c":"24320.92",
                "h":"24350",
                "l":"24170.08",
                "v":"61.75980004"
             }
          },
    
  • Change array-array to a Map aka a JSON object (assuming first value is unique):

    "items":{
          "1530871200000":{
                "o":"24174.11",
                "c":"24320.92",
                "h":"24350",
                "l":"24170.08",
                "v":"61.75980004"
          },
    
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • I have tried your way and it still doesn't work, the error that is displayed, it is "java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $". So the POJO class is bad ? in retrofit2 my call looks like this Call >> chartApi3(); – Mich Jul 14 '18 at 19:45
  • @Mich Edit the question and show what you've done. I was saying to change field `items` from type `ItemElement[][]` to type `Object[][]`. – Andreas Jul 14 '18 at 20:37
  • Json is already parsing well, thank you for your help ;) However, I cannot access individual objects. I need this response.body().getItems().get(0).getC(); – Mich Jul 14 '18 at 21:14
  • @Mich Now that `items` is a `List>`, why would you think `getItems().get(0).getC()` would work? `getItems()` returns `List>`, so `get(0)` returns `List`, and `List` does have a `getC()` method. The inner list has two elements, and the second is the `ItemClass`, so you need to add `get(1)` and cast, i.e. `((ItemClass) response.body().getItems().get(0).get(1)).getC()` – Andreas Jul 14 '18 at 22:20
  • rejects the following error 'java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.bitbay.mbart.bitbayapp.dataModel.chart.ItemClass' – Mich Jul 14 '18 at 22:47