Converting ZonedDateTime type to Gson

后端 未结 4 1898
梦谈多话
梦谈多话 2021-01-18 10:33

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

相关标签:
4条回答
  • 2021-01-18 10:36
    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();
    
    0 讨论(0)
  • Your solution posted doesn't work because ZonedDateTime's Json serialization is not a Json primitive but a Json object that contains more than a single element. It needs to develop a bit and here is a completed solution:

    public Gson gson() {
        return new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, new JsonDeserializer<ZonedDateTime>() {
            @Override
            public ZonedDateTime deserialize(JsonElement json, Type type,
                    JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
    
                JsonObject jsonObj = json.getAsJsonObject();
    
                JsonObject dateTime = jsonObj.getAsJsonObject("dateTime");
                JsonObject date = dateTime.getAsJsonObject("date");
                int year = date.get("year").getAsInt();
                int month = date.get("month").getAsInt();
                int day = date.get("day").getAsInt();
    
                JsonObject time = dateTime.getAsJsonObject("time");
                int hour = time.get("hour").getAsInt();
                int minute = time.get("minute").getAsInt();
                int second = time.get("second").getAsInt();
                int nano = time.get("nano").getAsInt();
    
                JsonObject zone = jsonObj.getAsJsonObject("zone");
                String id = zone.get("id").getAsString();
    
                return ZonedDateTime.of(year, month, day, hour, minute, second, nano, ZoneId.of(id));
            }
        }).create();
    }
    
    0 讨论(0)
  • 2021-01-18 10:44

    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.

    0 讨论(0)
  • 2021-01-18 10:49

    I have fixed it,This is the code after updates

    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);
    
            Gson gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, new JsonDeserializer<ZonedDateTime>() {
                @Override
                public ZonedDateTime deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
                    return ZonedDateTime.parse(json.getAsJsonPrimitive().getAsString());
                }
                }).create();
    
            Type listType =  new TypeToken<List<Scorefactor>>() {}.getType();
    
            List<Scorefactor> scorefactors = gson.fromJson(output,listType);
    
    0 讨论(0)
提交回复
热议问题