Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting ZonedDateTime type to Gson

I have rest service that return arraylist of object,and I have implemented jersy restful client to execute it,but I have problem in converting ZonedDateTime type to json so I get this error

 Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 231 path $[0].lastmodifieddate

How can i fix this problem?

lastmodifieddate column in entity

 @Column(name = "lastmodifieddate")
 private ZonedDateTime lastmodifieddate;

 //getter and setter

rest service

@RequestMapping(value = "/getScoreFactor",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
public List<Scorefactor> getScoreFactor() throws JSONException {
    return scoreService.getScoreFactor();
}   

jersy restful client

  try {

        Client client = Client.create();
        WebResource webResource = client
           .resource("http://localhost:8080/adap/api/getScoreFactor");
        ClientResponse response = webResource.accept("application/json")
                   .get(ClientResponse.class);

        String output =  response.getEntity(String.class);

        System.out.println("output--"+output);
        Type listType =  new TypeToken<List<Scorefactor>>() {}.getType();

        List<Scorefactor> scorefactors = new Gson().fromJson(output,listType);

        System.out.println(scorefactors);

    } catch (Exception e) {

        e.printStackTrace();

}    
like image 855
Ali-Alrabi Avatar asked Apr 04 '16 16:04

Ali-Alrabi


2 Answers

public static final Gson GSON = new GsonBuilder()
    .registerTypeAdapter(ZonedDateTime.class, new TypeAdapter<ZonedDateTime>() {
        @Override
        public void write(JsonWriter out, ZonedDateTime value) throws IOException {
            out.value(value.toString());
        }

        @Override
        public ZonedDateTime read(JsonReader in) throws IOException {
            return ZonedDateTime.parse(in.nextString());
        }
    })
    .enableComplexMapKeySerialization()
    .create();
like image 104
pomo Avatar answered Sep 18 '22 18:09

pomo


There are at least two ways to achieve this:

1) Gson with JsonDeserializer

Little changes within your code:

Type listType =  new TypeToken<List<Scorefactor>>() {}.getType();
List<Scorefactor> scorefactors = new GsonBuilder()
            .registerTypeAdapter(ZonedDateTime.class, GsonHelper.ZDT_DESERIALIZER)
            .create()
            .fromJson(output, listType);

Helper Class

class GsonHelper {

    public static final JsonDeserializer<ZonedDateTime> ZDT_DESERIALIZER = new JsonDeserializer<ZonedDateTime>() {
        @Override
        public ZonedDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
            JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
            try {

                // if provided as String - '2011-12-03T10:15:30+01:00[Europe/Paris]'
                if(jsonPrimitive.isString()){
                    return ZonedDateTime.parse(jsonPrimitive.getAsString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
                }

                // if provided as Long
                if(jsonPrimitive.isNumber()){
                    return ZonedDateTime.ofInstant(Instant.ofEpochMilli(jsonPrimitive.getAsLong()), ZoneId.systemDefault());
                }

            } catch(RuntimeException e){
                throw new JsonParseException("Unable to parse ZonedDateTime", e);
            }
            throw new JsonParseException("Unable to parse ZonedDateTime");
        }
    };

}

2) Use MessageBodyReader & XMLAdapter

Changes for your Client implementation:

ClientConfig config = new DefaultClientConfig();
config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(config);

List<Scorefactor> result = client.resource("http://localhost:8080/adap/api/getScoreFactor"")
                .accept("application/json")
                .get(ClientResponse.class)
                .getEntity(new GenericType<List<Scorefactor>>(){});

System.out.println(result);

You might need to import jersey-json for this

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-bundle</artifactId>
    <version>1.19.1</version>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>1.19.1</version>
</dependency>

Btw, why do you use 1.*?

Your Scorefactor

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Scorefactor implements Serializable {

    @Column(name = "lastmodifieddate")
    @XmlElement(name = "lastmodifieddate")
    @XmlJavaTypeAdapter(ZonedDateTimeToStringXmlAdapter.class)
    private ZonedDateTime lastmodifieddate;

    //...

ZonedDateTime from / to String (recommended)

public class ZonedDateTimeToStringXmlAdapter extends XmlAdapter<String, ZonedDateTime> {

    @Override
    public ZonedDateTime unmarshal(final String v) throws DateTimeParseException {
        return ZonedDateTime.parse(v);
    }

    @Override
    public String marshal(final ZonedDateTime v) throws Exception {
        return v.toString();
    }

}

ZonedDateTime from / to Long

public class ZonedDateTimeToLongXmlAdapter extends XmlAdapter<Long, ZonedDateTime> {

    @Override
    public ZonedDateTime unmarshal(final Long v) throws DateTimeParseException {
        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(v.longValue()), ZoneId.systemDefault()); 
    }

    @Override
    public Long marshal(final ZonedDateTime v) throws Exception {
        return Long.valueOf(v.toInstant().toEpochMilli());
    }

}

You could also build your own MessageBodyReader/MessageBodyWriter or use other implementations like Moxy.

I would like to recommend to use Jersey 2.*.

Hope this was helpful somehow. Have a nice day.

like image 20
zyexal Avatar answered Sep 22 '22 18:09

zyexal