在 java 上使用 Avro 发送和接收日期类型

send and receive Date type with Avro on java

我可以用 Apache Avro 发送和接收日期类型吗?我一直没能找到任何东西。只有我发现的东西说在架构中使用 int 和 logicalType of Date。但这会导致接收端出现另一个 int。我仍然需要将其转换为日期。

我正在尝试从 Apache Kafka 生产者发送日期并在 Kafka 消费者中接收。

如果没有其他方法,那么我是否必须始终将日期转换为 int,然后返回给消费者。这篇文章展示了如何做到这一点:

Get the number of days, weeks, and months, since Epoch in Java

序列化器代码:-

@Override
    public byte[] serialize(String topic, T data) {
        try {
            byte[] result = null;

            if (data != null) {
                logger.debug("data='{}'" +  data);

                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                BinaryEncoder binaryEncoder =
                        EncoderFactory.get().binaryEncoder(byteArrayOutputStream, null);

                DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(data.getSchema());
                datumWriter.write(data, binaryEncoder);

                binaryEncoder.flush();
                byteArrayOutputStream.close();

                result = byteArrayOutputStream.toByteArray();
                byteArrayOutputStream.close();
                logger.debug("serialized data='{}'" +  DatatypeConverter.printHexBinary(result));
            }
            return result;
        } catch (IOException ex) {
            throw new SerializationException(
                    "Can't serialize data='" + data + "' for topic='" + topic + "'", ex);
        }
    }

解串器代码:-

    @Override
    public T deserialize(String topic, byte[] data) {
        try {
            T result = null;

            if (data != null) {
                logger.debug("data='{}'" + DatatypeConverter.printHexBinary(data));

                DatumReader<GenericRecord> datumReader =
                        new SpecificDatumReader<>(targetType.newInstance().getSchema());
                Decoder decoder = DecoderFactory.get().binaryDecoder(data, null);

                result = (T) datumReader.read(null, decoder);
                logger.debug("deserialized data='{}'" + result);                
            }
            return result;
        } catch (Exception ex) {
            throw new SerializationException(
                    "Can't deserialize data '" + Arrays.toString(data) + "' from topic '" + topic + "'", ex);
        }
    }

架构文件:-

{"namespace": "com.test",
  "type": "record",
  "name": "Measures",
  "fields": [  
    {"name": "transactionDate", "type": ["int", "null"], "logicalType" : "date" }
   ]
}

而这两个只是在生产者和消费者配置中定义为序列化器和反序列化器类。

我没有使用过 Apace Avro 或 Apache Kafka,但也许这会有所帮助……

Is there away I can send and receive Date type with Apache Avro

查看维基百科页面,Avro 中没有定义 Date 类型:

Avro schemas are defined using JSON. Schemas are composed of primitive types (null, boolean, int, long, float, double, bytes, and string) and complex types (record, enum, array, map, union, and fixed).

JSON also lacks date-time types.

ISO 8601

在没有提供 date-time 支持的情况下,我建议使用标准 ISO 8601 格式将 date-time 值序列化为文本。这些格式旨在实用:易于机器解析,易于跨文化阅读,同时避免歧义。

对于 date-only 值,格式为 YYYY-MM-DD。 2018 年 1 月 23 日将是 2018-01-23

java.time

java.time classes 在 parsing/generating 字符串时默认使用 ISO 8601 格式。

LocalDate class 代表 date-only 值,没有 time-of-day 和时区。

LocalDate.of( 2018 , Month.JANUARY , 23 )
         .toString()                              // Generating a string in standard format.

2018-01-23

LocalDate ld = LocalDate.parse( "2018-01-23" ) ;  // Parsing a string in standard format.

Count-from-epoch

我不建议跟踪 date-time 值 epoch reference 的计数。但是,如果您决定这样做,java.time classes 可以提供帮助。

1970-01-01 的纪元参考日期定义为常量LocalDate.EPOCH

获取自该纪元参考以来的天数。

long daysSinceEpoch = ld.toEpochDay() ;

17554

解析纪元以来的天数。将 17,554 天添加到 1970-01-01 结果为 2018-01-23。

LocalDate ld = LocalDate.ofEpochDay( 17_554L ) ;  // 1970-01-01 + 17,554 days = 2018-01-23

你可以明白为什么我不推荐这种count-from-epoch方法:阅读和调试2018-01-23比解密17554容易得多。

Joda-Time

Apache Avro 包含一个 adapter class for Joda-Time types (ticket AVRO-1672)。我不知道这样的适配器是否是为 java.time 类型构建的。

Joda-Time 项目是 java.time 框架的前身 Java。该项目现在在 maintenance-mode,作者建议迁移到 java.time classes.


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

  • Java SE 8, Java SE 9,及以后
    • Built-in。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and Java SE 7
    • java.time 的大部分功能是 back-ported 到 Java ThreeTen-Backport 中的 6 和 7。
  • Android
    • Android java.time classes.
    • 捆绑实施的更高版本
    • 对于较早的Android,ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.