代码之家  ›  专栏  ›  技术社区  ›  Chad Schultz

有没有办法窥视GSON或倒带nextString中的值?

  •  0
  • Chad Schultz  · 技术社区  · 6 年前

    我用的是GSON,Java,Kotlin和Android。

    TypeAdapter

    我仍然需要处理许多标准格式的日期以及。

    我现在有:

    private class DateTypeAdapterFactory implements TypeAdapterFactory {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            final TypeAdapter<T> delegate = (TypeAdapter<T>)gson.getDelegateAdapter(this, type);
            if (!Date.class.isAssignableFrom(type.getRawType())) return null;
            //TODO: maybe avoid creating a new object on each method call?
            return (TypeAdapter<T>)new TypeAdapter<Date>() {
                public Date read(JsonReader reader) throws IOException {
                    if (reader.peek() == JsonToken.NULL) {
                        reader.nextNull();
                        return null;
                    }
                    String dateString = reader.nextString();
    
                    try {
                        if (yearMonthDayPattern.matcher(dateString).matches()) {
                            return yearMonthDayFormat.parse(dateString);
                        } else if (dateHourMinutePattern.matcher(dateString).matches()) {
                            return dateHourMinuteFormat.parse(dateString);
                        } else {TypeToken<Date>() {});
                            return (Date)delegate.read(reader);
                        }
                    } catch (ParseException e) {
                        Timber.wtf(e);
                        return null;
                    }
                }
    
                @Override
                public void write(JsonWriter out, Date value) throws IOException {
                    delegate.write(out, (T)value);
                }
            };
        }
    }
    

    nextString() 为了检查它是否是一个奇怪的格式,如果是,哪一个。但是当我将常规日期值传递给 delegate 对于默认处理 代表 假定尚未调用该值,并尝试推进流,从而导致错误。我怎样才能解决这个问题?

    如果有一种方法可以观察值而不消耗它,我可以调用它,在前两个If语句中检查该值,然后调用 下一个字符串() peek() 似乎只返回一个类型,而不是一个值。

    如果有办法“倒流”,我可以打电话给你 与我现在所做的完全相同,然后在将调用传递给代理之前将其倒带一次。但我没有看到这样的方法 JsonReader 应用程序编程接口。

    3 回复  |  直到 6 年前
        1
  •  0
  •   Chad Schultz    6 年前

    我找不到一种方法来读取下一个值而不使用它,或者倒带流以取消使用它。我也找不到其他方法来处理委托以访问默认行为。我确实找到了执行默认日期解析的类: https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java

    不幸的是,尽管它的构造函数允许传递一个额外的日期格式,但不能传递两个,这正是我所需要的。这个班是 final 我需要的方法是 private

    幸运的是,我注意到 deserializeToDate() 方法将实际获取原始日期字符串并将其设置为 Exception 当它不能解析值时抛出。这意味着我可以调用默认的解析器,如果它失败了,就把这个日期字符串拉出来,然后按我想要的方式解析它。

    这就是我最终得到的结果,似乎效果不错:

    private class DateTypeAdapterFactory implements TypeAdapterFactory {
        private DateFormat yearMonthDayFormat = DateFormatters.getDateFormatInUserTimeZone(FORMAT_TYPE_YYYY_MM_DD);
        private DateFormat dateHourMinuteFormat = DateFormatters.getDateFormatInUserTimeZone(DateFormatters.FORMAT_TYPE_ISO8601_NO_SECONDS);
    
        private Pattern yearMonthDayPattern = Pattern.compile("^\\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$");
        private Pattern dateHourMinutePattern = Pattern.compile(
                "[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T(2[0-3]|[01][0-9]):[0-5][0-9]");
    
        @SuppressWarnings("unchecked")
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
            if (!Date.class.isAssignableFrom(type.getRawType())) return null;
    
            return (TypeAdapter<T>) new TypeAdapter<Date>() {
                public Date read(JsonReader reader) throws IOException {
                    if (reader.peek() == JsonToken.NULL) {
                        reader.nextNull();
                        return null;
                    }
    
                    try {
                        return (Date) delegate.read(reader);
                    } catch (JsonSyntaxException jse) {
                        // DefaultDateTypeAdapter.deserializeToDate sets the date string as the JsonSyntaxException message,
                        // allowing us to try parsing it our own way.
                        String dateString = jse.getMessage();
    
                        try {
                            // If default date parsing fails, try one of the two funky new formats. Here we're using
                            // precompiled regexes for performance, but we could also try parsing and checking for
                            // ParseExceptions - that's what DefaultDateTypeAdapter does.
                            if (yearMonthDayPattern.matcher(dateString).matches()) {
                                return yearMonthDayFormat.parse(dateString);
                            } else if (dateHourMinutePattern.matcher(dateString).matches()) {
                                return dateHourMinuteFormat.parse(dateString);
                            } else {
                                throw jse;
                            }
                        } catch (ParseException pe) {
                            Timber.wtf(pe);
                            return null;
                        }
                    }
                }
    
                @Override
                public void write(JsonWriter out, Date value) throws IOException {
                    delegate.write(out, (T)value);
                }
            };
        }
    }
    
        2
  •  0
  •   user10339056    6 年前

    您不需要任何对DefaultDateTypeAdapter的显式引用,因为委托仍然可以。而且,流式阅读的整体和单点都是阅读 一旦 无需倒带(如果不需要缓冲,为什么需要付费?复卷的深度是多少?如果缓冲的JSON令牌太大而无法保存在内存中(特别是对于大型对象和字符串),该怎么办。一旦你消费了一个值,你可以选择如何处理它:丢弃它,打印出来,保存它,存储它,不管它是什么。完全由你决定。现在,既然已经使用了它,那么如果将该值转换为JSON树表示呢?一般来说,它是一个格式良好的JSON值,不需要重新读取,在这里倒带只是不适用的:这只是内存中的一个表示,您可以根据需要以任意顺序遍历它。话虽如此,看看 fromJsonTree . 它可以使用树,将它们转换为底层类型适配器可以使用的流(而且它们甚至不知道它们使用的不是流,而是树)。所以,你唯一需要改变的就是

    return (Date)delegate.read(reader);
    

    retutn (Date)delegate.fromJsonTree(new JsonPrimitive(dateString));
    

    (lsh)

        3
  •  0
  •   pirho    6 年前

    简单的创建和注册怎么样 JsonDeserializer<Date> ? 比如:

    public class DateDeserializer implements JsonDeserializer<Date> {
        // for default date parsing
        private final Gson innerGgson = new Gson();
    
        @Override
        public Date deserialize(JsonElement jsonElement, Type type, 
                                JsonDeserializationContext jsonDeserializationContext)
                throws JsonParseException {
    
            try {
                return innerGgson.fromJson(jsonElement, Date.class);
            } catch (Exception e) {
                // default parsing failed, try other formats
            }
    
            return parseDate(jsonElement.getAsString());
        }
    
        // all the possible date formats listed here 
        private static final DateFormat[] DATE_FORMATS = new DateFormat[] {
                new SimpleDateFormat("MM/dd/yyyy"),
                new SimpleDateFormat("ddMMyyyy") };
    
        // as an example, in this method, first try to instantiate all the 'normal' 
        // dateformats then something like it has now:
        // trying all the date formats, except ticks that then can be tried also
        // separately
        private Date parseDate(String dateString) {
            for (DateFormat df : DATE_FORMATS) {
                try {
                    return df.parse(dateString);
                } catch (ParseException e) {
                    // just try the next
                }
            }
            // finally, if not parsed return null or throw something
            return null;
        }
    
    }
    

    注册:

    Gson gson = new GsonBuilder()
        .registerTypeAdapter(Date.class, new DateDeserializer())
        .create();
    

    我想你仍然意识到,如果你获得的格式可以解释为多个格式,那么就没有解决方案 Date s、 意思是如果你 01/02/2018 它是 2018年1月2日 2018年2月1日 除非你有一些基本假设?