3

TL;DR:

My questions are:

1 - How can I make an Adapter for the "timestamp": 1515375392.225 to ZonedDateTime.

2 - How can I register the List<Report> adapter in the moshi object Builder if I need the moshi object to get this adapter, according to the documentation?


My JSON string has the following structure:

[
  {
    "id": 0,
    "location": {
      "latitude": -22.967049,
      "longitude": -43.19096
    },
    "timestamp": 1515375392.225
  },
  {
    "id": 0,
    "location": {
      "latitude": -22.965845,
      "longitude": -43.191102
    },
    "timestamp": 1515375392.225
  },
.......
}]

The timestamp is an automatic conversion made by the Jackson JavaTimeModule, it converts a ZonedDateTime to a timestamp String in the form of a decimal number representing the seconds and nanoseconds from an Instant.

In order to parse the JSON timestamp String, I made the following Moshi adapter:

public class ZonedDateTimeAdapter {
    @FromJson ZonedDateTime fromJson(String timestamp) {
        int decimalIndex = timestamp.indexOf('.');
        long seconds = Long.parseLong(timestamp.substring(0, decimalIndex));
        long nanoseconds = Long.parseLong(timestamp.substring(decimalIndex));

        return Instant.ofEpochSecond(seconds, nanoseconds).atZone(ZoneId.systemDefault());
    }

    @ToJson String toJson(ZonedDateTime zonedDateTime) {
        Instant instant = zonedDateTime.toInstant();
        return instant.getEpochSecond() + "." + instant.getNano();
    }
}

And then I register this adapter as:

Type type = Types.newParameterizedType(List.class, Report.class);

Moshi moshi = new Moshi.Builder().add(new ZonedDateTimeAdapter()).build();
JsonAdapter<List<Report>> reportAdapter = moshi.adapter(type);

retrofit = new Retrofit.Builder()
                       .baseUrl(BASE_URL)
                       .addConverterFactory(MoshiConverterFactory.create(moshi))
                       .build();

The problem is, when I call my webservice using Retrofit, I get the following Exception:

com.squareup.moshi.JsonDataException: java.lang.NumberFormatException: For input string: ".067000000" at $[0].timestamp

(keep in mind the nanoseconds .067000000 here won't be the same as the JSON example that I gave before, since they called the webservice at different times).

I tried to place a breakpoint on my ZonedDateTimeAdapter, but it's never being called. But it's influencing Moshi, because if I remove it from the Moshi.Builder, the error changes to:

Caused by: java.lang.IllegalArgumentException: Cannot serialize abstract class org.threeten.bp.ZoneId

I also tried to change the ZonedDateTimeAdapter to deal with Double instead of String, but it just changes the error message to:

com.squareup.moshi.JsonDataException: java.lang.NumberFormatException: For input string: ".515376840747E9" at $[0].timestamp

So, basically, I have a bunch of changing error messages and no idea what am I doing wrong. I followed the Moshi documentation on Custom Adapters and I don't know what else to do.

Michel Feinstein
  • 13,416
  • 16
  • 91
  • 173
  • It's not anything to do with Moshi or Retrofit. Take a look at the cause in the stacktrace to see which line is giving the NumberFormatException. It looks like you are trying to parse a Long from a floating point number. – Eric Cochran Jan 08 '18 at 10:35
  • I placed a breakpoint on my Adapter, the breakpoint is never hit. – Michel Feinstein Jan 08 '18 at 11:47
  • Also, despite the JSON having a floating point number, the Moshi documentation says that Moshi will convert the Adapter input to the type that I choose: `Note that the method annotated with @FromJson does not need to take a String as an argument. Rather it can take input of any type and Moshi will first parse the JSON to an object of that type and then use the @FromJsonmethod to produce the desired final value.` and I chose a String, so I am parsing from a String. – Michel Feinstein Jan 08 '18 at 12:02

1 Answers1

1

Your JSON adapter’s @ToJson methods is accepting a String but the timestamp is a number. Either change this to be a number (ie. double) or pass a JsonReader instead of a String and read the number out yourself. In this case you can call reader.nextString().

Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
  • I tried to make a double, as I stated in the question, the error changes to: `com.squareup.moshi.JsonDataException: java.lang.NumberFormatException: For input string: ".515376840747E9" at $[0].timestamp` – Michel Feinstein Jan 08 '18 at 15:55
  • Also, the documentations says that Moshi will `parse the JSON to an object of the type `, which led me to conclude that Moshi will give me the value as a string directly from the JSON, and not a number. – Michel Feinstein Jan 08 '18 at 15:59
  • Is it possible that Moshi doesn't handle nicely a very big Double? – Michel Feinstein Jan 08 '18 at 16:26
  • I ended up using a `JsonReader`. Neither string nor double works, all of them had exceptions as I showed in my post. Numbers should be able to be automatically presented as strings, just as `JsonReader` does. – Michel Feinstein Jan 08 '18 at 18:32