10

I am trying to parse a json string back to a Java object with gson. But I keep getting the above mentioned error:

Expected BEGIN_OBJECT but was STRING.

I saw there are some SO questions with this error, but none of them seems to be applicable to my situation.

My Java Code is the following:

Gson gson = new Gson();
Payment p = gson.fromJson(paymentJson, Payment.class);

where paymentJson is the JSON-String of a Payment object, which i get over an http post.

The Json-String Im using to test it is the following:

{
"creationDate": "2013-04-10T09:59:45.890+02:00",
"beneficiary": "Heat Hot Sauces",
"amount": 31.54,
"ibanSender": "CH190020600000DEMO303",
"paymentType": "ACCOUNT_TO_ACCOUNT",
"executionDate": "2013-04-10",
"fees": 0,
"ibanReceiver": "CH730020600000DEMO301",
"status": {
    "code": "PENDING"
},
"currency": "CHF",
"description": "Sudden Death Sauce",
"id": "25202",
"total": 31.54

}

And I checked the validity of the JSON with JsonLint, its valid json.

I know the other side which sends me the json data over http uses also GSON and the same class "Payment", but I'm not able/allowed to change the code there.

The Payment class looks like this:

public class Payment {

  private String id;
  private PaymentType paymentType;
  private PaymentCategory paymentCategory;
  private String ibanSender;
  private LocalDate executionDate;
  private DateTime creationDate;
  private String currency;
  private Double amount;
  private Double fees;
  private PaymentStatus status;
  private String description;
  private Double total;

  // attributes which identifiers the beneficiary of the payment
  private String ibanReceiver;
  private String accountNumber;
  private String swift;
  private String referenceNumber;
  private PaymentFeesChargingCode feesCharging;
  private String beneficiary;

  // the following 5 beneficiary fields can be used for address (beneficiary_5
  // should be used for country)
  private String beneficiary_1;
  private String beneficiary_2;
  private String beneficiary_3;
  private String beneficiary_4;
  private String beneficiary_5; // country
  // the reference to the StandingOrder which created this payment, or null
  private String standingOrderId;

  public Payment() {
    super();
  }
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public PaymentType getPaymentType() {
    return paymentType;
  }
  public void setPaymentType(PaymentType paymentType) {
    this.paymentType = paymentType;
  }
  public String getIbanSender() {
    return ibanSender;
  }
  public void setIbanSender(String ibanSender) {
    this.ibanSender = ibanSender;
  }
  public LocalDate getExecutionDate() {
    return executionDate;
  }
  public void setExecutionDate(LocalDate executionDate) {
    this.executionDate = executionDate;
  }
  public DateTime getCreationDate() {
    return creationDate;
  }
  public void setCreationDate(DateTime creationDate) {
    this.creationDate = creationDate;
  }
  public String getCurrency() {
    return currency;
  }
  public void setCurrency(String currency) {
    this.currency = currency;
  }
  public Double getAmount() {
    return amount;
  }
  public void setAmount(Double amount) {
    this.amount = amount;
  }
  public Double getFees() {
    return fees;
  }
  public void setFees(Double fees) {
    this.fees = fees;
  }
  public PaymentStatus getStatus() {
    return status;
  }
  public void setStatus(PaymentStatus status) {
    this.status = status;
  }
  public String getDescription() {
    return description;
  }
  public void setDescription(String description) {
    this.description = description;
  }
  public Double getTotal() {
    return total;
  }
  public void setTotal(Double total) {
    this.total = total;
  }
  public String getIbanReceiver() {
    return ibanReceiver;
  }
  public void setIbanReceiver(String ibanReceiver) {
    this.ibanReceiver = ibanReceiver;
  }
  public String getAccountNumber() {
    return accountNumber;
  }
  public void setAccountNumber(String accountNumber) {
    this.accountNumber = accountNumber;
  }
  public String getSwift() {
    return swift;
  }
  public void setSwift(String swift) {
    this.swift = swift;
  }
  public String getReferenceNumber() {
    return referenceNumber;
  }
  public void setReferenceNumber(String referenceNumber) {
    this.referenceNumber = referenceNumber;
  }
  public PaymentFeesChargingCode getFeesCharging() {
    return feesCharging;
  }
  public void setFeesCharging(PaymentFeesChargingCode feesCharging) {
    this.feesCharging = feesCharging;
  }
  public String getBeneficiary() {
    return beneficiary;
  }
  public void setBeneficiary(String beneficiary) {
    this.beneficiary = beneficiary;
  }
  public String getBeneficiary_1() {
    return beneficiary_1;
  }
  public void setBeneficiary_1(String beneficiary_1) {
    this.beneficiary_1 = beneficiary_1;
  }
  public String getBeneficiary_2() {
    return beneficiary_2;
  }
  public void setBeneficiary_2(String beneficiary_2) {
    this.beneficiary_2 = beneficiary_2;
  }
  public String getBeneficiary_3() {
    return beneficiary_3;
  }
  public void setBeneficiary_3(String beneficiary_3) {
    this.beneficiary_3 = beneficiary_3;
  }
  public String getBeneficiary_4() {
    return beneficiary_4;
  }
  public void setBeneficiary_4(String beneficiary_4) {
    this.beneficiary_4 = beneficiary_4;
  }
  public String getBeneficiary_5() {
    return beneficiary_5;
  }
  public void setBeneficiary_5(String beneficiary_5) {
    this.beneficiary_5 = beneficiary_5;
  }

  @Override
  public String toString() {
    return "Payment [id=" + id + ", ibanSender=" + ibanSender + ", ibanReceiver=" + ibanReceiver + ", beneficiary=" + beneficiary + ", executionDate=" + executionDate + ", creationDate=" + creationDate + ", currency=" + currency + ", amount=" + amount + ", fees=" + fees + ", status=" + status + ", description=" + description + ", total=" + total + "]";
  }

  public PaymentCategory getPaymentCategory() {
    return paymentCategory;
  }
  public void setPaymentCategory(PaymentCategory paymentCategory) {
    this.paymentCategory = paymentCategory;
  }
  public String getStandingOrderId() {
    return standingOrderId;
  }
  public void setStandingOrderId(String standingOrderId) {
    this.standingOrderId = standingOrderId;
  }
}

EDIT: here is the stack-trace:

java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 48
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172)
com.google.gson.Gson.fromJson(Gson.java:795)
com.google.gson.Gson.fromJson(Gson.java:761)
com.google.gson.Gson.fromJson(Gson.java:710)
com.google.gson.Gson.fromJson(Gson.java:682)
ch.ti8m.bank.payment.scanbuy.controller.ScanAndBuyController.preparePayment(ScanAndBuyController.java:58)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:601)
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:822)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:180)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
java.lang.Thread.run(Thread.java:722)

Does anyone have an idea why I'm getting that error message?

Thanks in advance

4
  • 1
    private PaymentType paymentType; "paymentType": "ACCOUNT_TO_ACCOUNT", Payment type is an object, but the json represents it as a string. Post the entire stack trace and we can verify this. Commented Apr 12, 2013 at 13:41
  • @Deepak Bala: I posted the stack-trace. Thanks for pointing that out with the payment Type. PaymentType is an enum, containing entries like ACCOUNT_TO_ACCOUNT. How can i fix this? Commented Apr 12, 2013 at 13:49
  • Do you mean that you have to use that Payment class but you can't modify it? Commented Apr 13, 2013 at 0:00
  • @MikO Yes I have to use that Payment class, and can not change the class or the code that produces the JSON on the other side Commented Apr 15, 2013 at 7:24

2 Answers 2

35

GSON fails to parse the value of the creationDate field because it does not know how to handle Joda-Time DateTime objects.

Try to register a type adapter for this class using GsonBuilder's registerTypeAdapter method.

Gson gson = new GsonBuilder().registerTypeAdapter(DateTime.class, new JsonDeserializer<DateTime>() {
    @Override
    public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        return new DateTime(json.getAsString());
    }
}).create();
Sign up to request clarification or add additional context in comments.

2 Comments

I tried to add this type adapter to my class, it didnt compile, because it asked me to return DateTime rather than Date
I came here and found that this also works for other DateTime classes within Joda-Time. Tried this for LocalDate class and it works. Great work. +1
1

I have implemented a slightly different approach, since in my case I have problems both serializing and deserializing attributes of java.time.LocalDate in my Spring Boot project:

@Configuration
@EnableWebMvc
public class ApplicationConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        GsonHttpMessageConverter gsonConverter = new GsonHttpMessageConverter();
        gsonConverter.setGson(gson());
        converters.add(gsonConverter);
    }

    @Bean
    public Gson gson() {
        return new GsonBuilder()
                .serializeNulls()
                .registerTypeAdapter(LocalDate.class,
                        (JsonSerializer<LocalDate>) (localDate, type, context)
                        -> new JsonPrimitive(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))))
                .registerTypeAdapter(LocalDate.class,
                        (JsonDeserializer<LocalDate>) (jsonElement, type, context)
                        -> LocalDate.parse(jsonElement.getAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))
                .create();
    }
    ...
}

That configuration allows me not only having configured my GsonHttpMessageConverter with the adapters for LocalDate but also having a singleton bean for this Gson configuration available for my application.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.