我有一个 avro 模式,其中包含以下字段之一
{
"name" : "currency",
"type" : ["null","bytes"],
"logicalType": "decimal",
"precision": 9,
"scale": 4
},
我运行了 avro-tools jar 来创建 java 文件来表示模式。这产生了如下所示的属性:public java.nio.ByteBuffer currency;
在我的代码的其他地方,我将使用 BigDecimal
类型的货币值。
在创建此类的实例时,如何将 BigDecimal
值转换为预期的 ByteBuffer
?我可以只使用 ByteBuffer.toByteArray()
还是我需要做任何特殊的事情来确保它与 avro(以及可能正在读取数据的 Impala 等其他工具)兼容?
最佳答案
让我们从免责声明开始。虽然“逻辑类型”部分出现在大约 2014 年的规范中,但它还不受任何 Avro Java 版本的支持。
您可以决定声明一个符合规范的模式并将正确的字节放入该字段,但 Avro Java 不会帮助您(这与您省略逻辑类型相关字段完全一样)。
如何将 BigDecimal 值转换为预期的 ByteBuffer
文档指出:
A decimal logical type annotates Avro bytes or fixed types. The byte array must contain the two's-complement representation of the unscaled integer value in big-endian byte order. The scale is fixed, and is specified using an attribute.
可以用 Java 翻译为(从 Avro 1.8.0-rc2 复制粘贴):
public ByteBuffer toBytes(BigDecimal value, Schema schema, LogicalType type)
{
int scale = ((LogicalTypes.Decimal) type).getScale();
if (scale != value.scale()) {
throw new AvroTypeException("Cannot encode decimal with scale " +
value.scale() + " as scale " + scale);
}
return ByteBuffer.wrap(value.unscaledValue().toByteArray());
}
您可以阅读 BigDecimal 和 BigInteger 的 Javadoc 来检查 value.unscaledValue().toByteArray()
是否符合规范。
以类似的方式,您可以使用以下代码反序列化该字段:return new BigDecimal(new BigInteger(bytes), scale);
你应该使用逻辑类型吗?
如前言所述,如果您使用的是 Avro 1.7,则不会免费提供任何东西。您必须编写自己的(反)序列化程序,代码生成和反射(reflect)不支持此构造。使用它的唯一原因是遵守规范,并希望 future 的 Avro 版本能让您的生活更轻松。
Avro 1.8.0-rc2 包含一些代码来支持逻辑类型并引入新的逻辑类型。似乎为所有逻辑类型提供了(反)序列化器(参见 Conversion
和 Conversions
)并且转换已插入 GenericData。这意味着当您询问该字段的值时,您将收到一个 BigDecimal
实例。如果您正确注释该字段,ReflectData 似乎也能够生成预期的模式(但 AFAIK 没有为逻辑类型创建专用注释)。
但是,我不清楚 avro-compiler/codegen 是否已更新以支持逻辑类型。
关于java - 如何从 ByteBuffer 转换为 Avro 字节?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34866793/