【问题标题】:Converting ZonedDateTime type to Gson将 ZonedDateTime 类型转换为 Gson
【发布时间】:2016-07-24 07:43:07
【问题描述】:

我有返回对象数组列表的休息服务,并且我已经实现了 jersy restful 客户端来执行它,但是我在将 ZonedDateTime 类型转换为 json 时遇到问题,所以我收到了这个错误

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

我该如何解决这个问题?

实体中的lastmodifieddate列

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

 //getter and setter

休息服务

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

jersy 安静的客户端

  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();

}    

【问题讨论】:

标签: java json rest jersey gson


【解决方案1】:

至少有两种方法可以做到这一点:

1) 带有 JsonDeserializer 的 Gson

代码中的小改动:

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

助手类

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) 使用 MessageBodyReader & XMLAdapter

您的客户端实施的更改:

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);

您可能需要为此导入 jersey-json

<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>

顺便说一句,你为什么使用 1.*?

你的得分因素

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

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

    //...

ZonedDateTime 从/到字符串(推荐)

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 从 / 到 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());
    }

}

您还可以构建自己的 MessageBodyReader/MessageBodyWriter 或使用 Moxy 等其他实现。

我想推荐使用 Jersey 2.*。

希望这对您有所帮助。祝你今天过得愉快。

【讨论】:

    【解决方案2】:

    我已经修复了,这是更新后的代码

    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);
    

    【讨论】:

      【解决方案3】:
      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();
      

      【讨论】:

        【解决方案4】:

        您发布的解决方案不起作用,因为 ZonedDateTime 的 Json 序列化不是 Json 原语,而是包含多个元素的 Json 对象。它需要开发一点,这里是一个完整的解决方案:

        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();
        }
        

        【讨论】:

        • 这个解决方案对我有用。但是,你知道为什么 gson 不能正确反序列化 ZonedDateTime 吗?
        • 因为 ZonedDateTime 的 JSON 序列化是一个包含多个元素的 JSON 对象。所以在覆盖方法deserialize中我们需要正确读取JSON对象比如JsonObject jsonObj = json.getAsJsonObject()。在最初的解决方案中,它是 json.getAsJsonPrimitive()。
        猜你喜欢
        • 2019-08-03
        • 2017-05-31
        • 2018-12-24
        • 2015-11-07
        • 2020-07-30
        • 2018-09-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多