3

I have a problem. I have the following JSON:

[
   {
      "Id":"8",
      "MasterAgentId":"0",
      "LastAgentStrategyId":"16116488969159",
      "BotState":"Active",
      "Owner":"Andre",
      "ExtraNotifiers":"",
      "Exchange":"lava",
      "DateTime":"2021-01-27 22:12:02",
      "AgentStatus":"Enabled",
      "ProtectiveOrdersEnabled":"yes",
      "Sim_Progress":"100",
      "Sim_DateTime":"2021-01-27 09:18:00",
      "Sim_Coin":"BTC",
      "Sim_Price":"31626.580000",
      "Sim_Profit":"1216.04",
      "Sim_Profit_Perc":"60.80",
      "Sim_StartDateTime":"2019-01-01 00:00:00",
      "LastAgentStrategyRun":"2020-12-19 17:40:30.531000"
   }
]

As you can see I have 4 DateTimes in the JSON: DateTime, Sim_DateTime, Sim_StartDateTime, LastAgentStrategyRun

Now I created a class for this that looks like this:

import java.time.LocalDateTime;

public class Agent {

    private int Id;
    private int MasterAgentId;
    private long LastAgentStrategyId;
    private AgentBotState BotState;
    private String Owner;
    private String ExtraNotifiers;
    private AgentExchanges Exchange;
    private LocalDateTime DateTime;
    private AgentStatus AgentStatus;
    private String ProtectiveOrdersEnabled;
    private String Sim_Progress;
    private String Sim_DateTime;
    private String Sim_Coin;
    private double Sim_Price;
    private double Sim_Profit;
    private double Sim_Profit_Perc;
    private LocalDateTime Sim_StartDateTime;
    private LocalDateTime LastAgentStrategyRun;
}

But when I deserialize the json using GSON:

Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<Agent>>() {}.getType();
ArrayList<Agent> agentList = new Gson().fromJson(jsonResponse, listType);

I get the following error:

But the code crashes with the following error:

Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 157 path $[0].DateTime
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:200)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:103)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:196)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:81)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:60)
    at com.google.gson.Gson.fromJson(Gson.java:810)
    at com.google.gson.Gson.fromJson(Gson.java:775)
    at com.google.gson.Gson.fromJson(Gson.java:724)
    at com.company.Main.main(Main.java:34)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 157 path $[0].DateTime
    at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:387)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:189)
    ... 9 more

Now when I remove all the datetimes from the json, it parses correctly with only a few weird warning:

WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by com.google.gson.internal.bind.ReflectiveTypeAdapterFactory (file:/C:/Users/Alexander/.m2/repository/com/google/code/gson/gson/2.3.1/gson-2.3.1.jar) to field java.time.LocalDateTime.date WARNING: Please consider reporting this to the maintainers of com.google.gson.internal.bind.ReflectiveTypeAdapterFactory WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release

How can I parse a datetime string to an object and how can I get rid of the warnings?

UPDATE

Afther using the following code:

Gson gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class, (JsonDeserializer<LocalDateTime>) (json, type, jsonDeserializationContext) -> {
    Instant instant = Instant.ofEpochMilli(json.getAsJsonPrimitive().getAsLong());
    return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}).create();

Type listType = new TypeToken<ArrayList<Agent>>() {}.getType();
ArrayList<Agent> agentList = gson.fromJson(jsonResponse, listType);

I get this error now:

Exception in thread "main" java.lang.NumberFormatException: For input string: "2021-01-27 22:56:01"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)
    at java.base/java.lang.Long.parseLong(Long.java:707)
    at java.base/java.lang.Long.parseLong(Long.java:832)
    at com.google.gson.JsonPrimitive.getAsLong(JsonPrimitive.java:238)
    at com.company.Main.lambda$main$0(Main.java:35)
    at com.google.gson.TreeTypeAdapter.read(TreeTypeAdapter.java:58)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:103)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:196)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:81)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:60)
    at com.google.gson.Gson.fromJson(Gson.java:810)
    at com.google.gson.Gson.fromJson(Gson.java:775)
    at com.google.gson.Gson.fromJson(Gson.java:724)
    at com.company.Main.main(Main.java:40)
Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
A. Vreeswijk
  • 822
  • 1
  • 19
  • 57
  • Possibly related? [Java 8 LocalDateTime deserialized using Gson](https://stackoverflow.com/q/22310143) – Pshemo Jan 27 '21 at 21:29
  • No, the difference is that he just wants to parse 1 element, but I parse the whole json to a class. – A. Vreeswijk Jan 27 '21 at 21:30
  • Updated my Answer again to simplify the DateTimeFormat parsing and take account of the different data formats you have – Andrew Jan 27 '21 at 23:50

3 Answers3

3

You need to register LocalDateTime: e.g.replace Gson gson = new Gson(); see code below:

Updated answer to parse a LocalDateTime using a date formatter, instead of creating an Instant. Also your date formats are not consistent as some return seconds others milliseconds so I have provided two format alternatives.

Please see example code using a simplified Agent class

String jsonResponse = "[\n" +
            "   {\n" +
            "      \"DateTime\":\"2021-01-27 09:18:00\",\n" +
            "      \"Sim_StartDateTime\":\"2019-01-01 00:00:00\",\n" +
            "      \"LastAgentStrategyRun\":\"2020-12-19 17:40:30.531000\"\n" +
            "   }\n" +
            ']';

    Gson gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class, (JsonDeserializer<LocalDateTime>) (json, type, jsonDeserializationContext) -> {

        try{
            return LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        } catch (DateTimeParseException e){
            return LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"));
        }

    }).create();

    Type listType = new TypeToken<ArrayList<Agent>>() {}.getType();
    ArrayList<Agent> agentList = gson.fromJson(jsonResponse, listType);

    System.out.println(agentList);

and the Agent class

public class Agent {
    private LocalDateTime DateTime;
    private LocalDateTime Sim_StartDateTime;
    private LocalDateTime LastAgentStrategyRun;

    public LocalDateTime getDateTime() {
        return DateTime;
    }

    public void setDateTime(LocalDateTime dateTime) {
        DateTime = dateTime;
    }

    public LocalDateTime getSim_StartDateTime() {
        return Sim_StartDateTime;
    }

    public void setSim_StartDateTime(LocalDateTime sim_StartDateTime) {
        Sim_StartDateTime = sim_StartDateTime;
    }

    public LocalDateTime getLastAgentStrategyRun() {
        return LastAgentStrategyRun;
    }

    public void setLastAgentStrategyRun(LocalDateTime lastAgentStrategyRun) {
        LastAgentStrategyRun = lastAgentStrategyRun;
    }

    @Override
    public String toString() {
        return "Agent{" +
                "DateTime=" + DateTime +
                ", Sim_StartDateTime=" + Sim_StartDateTime +
                ", LastAgentStrategyRun=" + LastAgentStrategyRun +
                '}';
    }
}

and the output

[Agent{DateTime=2021-01-27T09:18, Sim_StartDateTime=2019-01-01T00:00, LastAgentStrategyRun=2020-12-19T17:40:30.531}]
Andrew
  • 2,598
  • 16
  • 27
0

You can use GsonUtils. But pay attention on the following issue: you have a different date/time format in this field. This is not correct, because to parse this filed, you have to set a date/time formatter with required template. I have changed formation in the LastAgentStrategyRun field and this is an example:

String json = "[\n" +
        "   {\n" +
        "      \"Id\":\"8\",\n" +
        "      \"MasterAgentId\":\"0\",\n" +
        "      \"LastAgentStrategyId\":\"16116488969159\",\n" +
        "      \"BotState\":\"Active\",\n" +
        "      \"Owner\":\"Andre\",\n" +
        "      \"ExtraNotifiers\":\"\",\n" +
        "      \"Exchange\":\"lava\",\n" +
        "      \"DateTime\":\"2021-01-27 22:12:02\",\n" +
        "      \"AgentStatus\":\"Enabled\",\n" +
        "      \"ProtectiveOrdersEnabled\":\"yes\",\n" +
        "      \"Sim_Progress\":\"100\",\n" +
        "      \"Sim_DateTime\":\"2021-01-27 09:18:00\",\n" +
        "      \"Sim_Coin\":\"BTC\",\n" +
        "      \"Sim_Price\":\"31626.580000\",\n" +
        "      \"Sim_Profit\":\"1216.04\",\n" +
        "      \"Sim_Profit_Perc\":\"60.80\",\n" +
        "      \"Sim_StartDateTime\":\"2019-01-01 00:00:00\",\n" +
        "      \"LastAgentStrategyRun\":\"2020-12-19 17:40:30\"\n" +
        "   }\n" +
        ']';

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
GsonDecorator gson = GsonHelper.createGsonDecorator(new GsonBuilderDecorator().withDateTimeFormatter(formatter));
List<Agent> agent = gson.readList(json, Agent.class);
Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
  • How do I install GsonUtils from GitHub? I am using IntelliJ. I can't find the maven library when I search for it – A. Vreeswijk Jan 27 '21 at 22:05
  • This time you have to download it, then build it using `gradle` and add `jar` to your project. I can add this tool to Maven if you are going to use it. – Oleg Cherednik Jan 27 '21 at 22:06
  • I really want to use it, because it looks way easier. Could you add it to maven? – A. Vreeswijk Jan 27 '21 at 22:07
  • 1
    Ok but you have to test it first for your json. I can see that you have different time formats in the fields. – Oleg Cherednik Jan 27 '21 at 22:10
  • I am struggling with installing it as gradle, could you upload it to Maven, so I can install it from there? It will probably work, because you tested it with my JSON! – A. Vreeswijk Jan 28 '21 at 14:21
0

Sometimes we have to define both Serializer and Deserializer.
Example

Gson gson = new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class, (JsonSerializer<LocalDateTime>) (value, type, context) ->
    new JsonPrimitive(value.format(DateTimeFormatter.ISO_DATE_TIME))
)
.registerTypeAdapter(LocalDateTime.class, (JsonDeserializer<LocalDateTime>) (jsonElement, type, context) ->
        LocalDateTime.parse(jsonElement.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ISO_DATE_TIME)
)
.create();
Adam
  • 1,796
  • 18
  • 19