35

我开始将自定义序列化机制迁移到协议缓冲区。将特别经常使用的一种数据类型是BigDecimal.

有谁知道在协议缓冲区中序列化它的好方法?我们当前的序列化例程BigDecimal.toPlainString()用于序列化和反序列化new BigDecimal(String)- 我假设有更好的方法。

我的猜测是将 a 定义BigDecimal为:

message BDecimal {
    required int32 scale = 1;
    required BInteger int_val = 2;
}

但我不太确定如何定义BigInteger- 也许使用它的toByteArray()方法?

4

3 回答 3

25

是的。您应该将 BigInteger 定义为 BigInteger.toByteArray() 。

我的猜测是 BigDecimal 将是:


message BDecimal {
  required int32 scale = 1;
  required BInteger int_val = 2;
}

而 BigInteger 可以定义为


message BInteger {
  required bytes value = 1;
}

处理 BigInteger 的代码是:


  BInteger write(BigInteger val) {
    BInteger.Builder builder = BInteger.newBuilder();
    ByteString bytes = ByteString.copyFrom(val.toByteArray());
    builder.setValue(bytes);
    return builder.build();
  }

  BigInteger read(BInteger message) {
    ByteString bytes = message.getValue();
    return new BigInteger(bytes.toByteArray());
  }
于 2009-06-27T03:25:57.850 回答
7

我最近和 OP 有同样的需求,并使用了与@notnoop 提出的类似的解决方案。在看到@stikkos 的评论后在这里发布:

您如何将 BigDecimal 转换为 BigInteger 并进行缩放?然后回来 ?

根据BigDecimal文档

的值为BigDecimal( unscaledVal× 10-scale ),根据精度和舍入模式设置进行舍入。

因此,java.math.BigDecimal可以考虑三个属性对 a 进行序列化:


现在,代码。

  1. Protobuf v3 消息定义:

    syntax = "proto3";
    
    message DecimalValue {
      uint32 scale = 1;
      uint32 precision = 2;
      bytes value = 3;
    }
    
  2. 如何将 a 序列化为java.math.BigDecimalProtobuf 消息:

    import com.google.protobuf.ByteString;
    import DecimalValue;
    
    java.math.BigDecimal bigDecimal = new java.math.BigDecimal("1234.56789");
    DecimalValue serialized = DecimalValue.newBuilder()
            .setScale(bigDecimal.scale())
            .setPrecision(bigDecimal.precision())
            .setValue(ByteString.copyFrom(bigDecimal.unscaledValue().toByteArray()))
            .build();
    
  3. 如何将 Protobuf 消息反序列化回java.math.BigDecimal

    java.math.MathContext mc = new java.math.MathContext(serialized.getPrecision());
    java.math.BigDecimal deserialized = new java.math.BigDecimal(
            new java.math.BigInteger(serialized.getValue().toByteArray()),
            serialized.getScale(),
            mc);
    
  4. 我们可以检查获得的java.math.BigDecimal产量是否与原始值相同,如下所示:

    assert bigDecimal.equals(deserialized);
    

注意:OP 假设有java.math.BigDecimal比使用普通的更好的方法来序列化 a String。在存储方面,这个解决方案更好。

例如,要序列化 ​​π 的前 100 位小数:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
System.out.println("DecimalValue: " + serialized.getSerializedSize());
System.out.println("String: " + StringValue.of(pi).getSerializedSize());

这产生:

DecimalValue: 48
String: 104
于 2020-09-10T08:11:59.907 回答
0

你为什么要改变它?仅仅因为您可以或是否有真正的需求(例如确认分析会话,序列化/反序列化需要大部分时间)。

我会使用一个字符串,只是因为它是内置的:)

如果字符串表示似乎是一个问题,那么建议的字节数组方法(将 BigDecimal/BigInteger 序列化为 ProtocolBuffers 的最佳方法是什么)对我来说似乎很好。

于 2009-06-28T10:02:49.513 回答