Avro无法在字段中反序列化具有逻辑类型的联合

时间:2020-08-29 02:33:14

标签: java apache-kafka avro avro-tools

Avro工具版本-1.9.2

这是我在反序列化Avro数据时面临的问题。字段为具有空值和逻辑类型的Union时,似乎Avro无法生成正确的POJO类。

当前行为:当avro架构的字段为null和逻辑类型的并集时。 Avro不添加转换类型数组,因此我们得到类强制转换异常。这是original.avsc

的问题

预期的行为:Avro应该能够序列化具有空值的逻辑类型。

看看下面的original.avsc,没有为任何字段创建conversions数组。由于原始字段数据为原始类型,而POJO字段基于逻辑类型,因此产生ClassCastException

original.avsc

{
    "type": "record",
    "name": "UserRecord",
    "namespace": "com.original",
    "fields": [
        {
            "name": "user",
            "type": {
                "type": "record",
                "name": "User",
                "fields": [
                    {
                        "name": "KEY_NBR",
                        "type": [
                            "null",
                            "string"
                        ],
                        "default": null
                    },
                    {
                        "name": "CRTE_TMS",
                        "type": [
                            "null",
                            {
                                "type": "long",
                                "logicalType": "timestamp-micros"
                            }
                        ],
                        "default": null
                    },
                    {
                        "name": "BIRTH_DT",
                        "type": [
                            "null",
                            {
                                "type": "int",
                                "logicalType": "date"
                            }
                        ],
                        "default": null
                    },
                    {
                        "name": "BAL_AMT",
                        "type": [
                            "null",
                            {
                                "type": "bytes",
                                "precision": 7,
                                "scale": 2,
                                "logicalType": "decimal"
                            }
                        ],
                        "default": null
                    }
                ]
            }
        },
        {
            "name": "beforeUser",
            "type": [
                "null",
                "User"
            ],
            "default": null
        }
    ]
}

基于Original.avsc的POJO



package com.original;

import org.apache.avro.generic.GenericArray;
import org.apache.avro.specific.SpecificData;
import org.apache.avro.util.Utf8;
import org.apache.avro.message.BinaryMessageEncoder;
import org.apache.avro.message.BinaryMessageDecoder;
import org.apache.avro.message.SchemaStore;

@org.apache.avro.specific.AvroGenerated
public class User extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
  private static final long serialVersionUID = 4336376255546547414L;
  public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"User\",\"namespace\":\"com.original\",\"fields\":[{\"name\":\"KEY_NBR\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"CRTE_TMS\",\"type\":[\"null\",{\"type\":\"long\",\"logicalType\":\"timestamp-micros\"}],\"default\":null},{\"name\":\"BIRTH_DT\",\"type\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}],\"default\":null},{\"name\":\"BAL_AMT\",\"type\":[\"null\",{\"type\":\"bytes\",\"precision\":7,\"scale\":2,\"logicalType\":\"decimal\"}],\"default\":null}]}");
  public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }

  private static SpecificData MODEL$ = new SpecificData();
static {
    MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.DateConversion());
    MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.TimestampMillisConversion());
  }

  private static final BinaryMessageEncoder<User> ENCODER =
      new BinaryMessageEncoder<User>(MODEL$, SCHEMA$);

  private static final BinaryMessageDecoder<User> DECODER =
      new BinaryMessageDecoder<User>(MODEL$, SCHEMA$);


  public static BinaryMessageEncoder<User> getEncoder() {
    return ENCODER;
  }


  public static BinaryMessageDecoder<User> getDecoder() {
    return DECODER;
  }

 
  public static BinaryMessageDecoder<User> createDecoder(SchemaStore resolver) {
    return new BinaryMessageDecoder<User>(MODEL$, SCHEMA$, resolver);
  }

  
  public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
    return ENCODER.encode(this);
  }

 
  public static User fromByteBuffer(
      java.nio.ByteBuffer b) throws java.io.IOException {
    return DECODER.decode(b);
  }

   private java.lang.CharSequence KEY_NBR;
   private java.time.Instant CRTE_TMS;
   private java.time.LocalDate BIRTH_DT;
   private java.nio.ByteBuffer BAL_AMT;

 
  public User() {}

  
  public User(java.lang.CharSequence KEY_NBR, java.time.Instant CRTE_TMS, java.time.LocalDate BIRTH_DT, java.nio.ByteBuffer BAL_AMT) {
    this.KEY_NBR = KEY_NBR;
    this.CRTE_TMS = CRTE_TMS;
    this.BIRTH_DT = BIRTH_DT;
    this.BAL_AMT = BAL_AMT;
  }

  public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; }
  public org.apache.avro.Schema getSchema() { return SCHEMA$; }
  // Used by DatumWriter.  Applications should not call.
  public java.lang.Object get(int field$) {
    switch (field$) {
    case 0: return KEY_NBR;
    case 1: return CRTE_TMS;
    case 2: return BIRTH_DT;
    case 3: return BAL_AMT;
    default: throw new org.apache.avro.AvroRuntimeException("Bad index");
    }
  }

  // Used by DatumReader.  Applications should not call.
  @SuppressWarnings(value="unchecked")
  public void put(int field$, java.lang.Object value$) {
    switch (field$) {
    case 0: KEY_NBR = (java.lang.CharSequence)value$; break;
    case 1: CRTE_TMS = (java.time.Instant)value$; break;
    case 2: BIRTH_DT = (java.time.LocalDate)value$; break;
    case 3: BAL_AMT = (java.nio.ByteBuffer)value$; break;
    default: throw new org.apache.avro.AvroRuntimeException("Bad index");
    }
  }

 
  public java.lang.CharSequence getKEYNBR() {
    return KEY_NBR;
  }


  /**
   * Sets the value of the 'KEY_NBR' field.
   * @param value the value to set.
   */
  public void setKEYNBR(java.lang.CharSequence value) {
    this.KEY_NBR = value;
  }

  
  public java.time.Instant getCRTETMS() {
    return CRTE_TMS;
  }


  /**
   * Sets the value of the 'CRTE_TMS' field.
   * @param value the value to set.
   */
  public void setCRTETMS(java.time.Instant value) {
    this.CRTE_TMS = value;
  }

  /**
   * Gets the value of the 'BIRTH_DT' field.
   * @return The value of the 'BIRTH_DT' field.
   */
  public java.time.LocalDate getBIRTHDT() {
    return BIRTH_DT;
  }


  public void setBIRTHDT(java.time.LocalDate value) {
    this.BIRTH_DT = value;
  }

  /**
   * Gets the value of the 'BAL_AMT' field.
   * @return The value of the 'BAL_AMT' field.
   */
  public java.nio.ByteBuffer getBALAMT() {
    return BAL_AMT;
  }


  /**
   * Sets the value of the 'BAL_AMT' field.
   * @param value the value to set.
   */
  public void setBALAMT(java.nio.ByteBuffer value) {
    this.BAL_AMT = value;
  }

  /**
   * Creates a new User RecordBuilder.
   * @return A new User RecordBuilder
   */
  public static com.original.User.Builder newBuilder() {
    return new com.original.User.Builder();
  }

 
  public static com.original.User.Builder newBuilder(com.original.User.Builder other) {
    if (other == null) {
      return new com.original.User.Builder();
    } else {
      return new com.original.User.Builder(other);
    }
  }


  public static com.original.User.Builder newBuilder(com.original.User other) {
    if (other == null) {
      return new com.original.User.Builder();
    } else {
      return new com.original.User.Builder(other);
    }
  }

  
  @org.apache.avro.specific.AvroGenerated
  public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<User>
    implements org.apache.avro.data.RecordBuilder<User> {

    private java.lang.CharSequence KEY_NBR;
    private java.time.Instant CRTE_TMS;
    private java.time.LocalDate BIRTH_DT;
    private java.nio.ByteBuffer BAL_AMT;

   
    private Builder() {
      super(SCHEMA$);
    }

    private Builder(com.original.User.Builder other) {
      super(other);
      if (isValidValue(fields()[0], other.KEY_NBR)) {
        this.KEY_NBR = data().deepCopy(fields()[0].schema(), other.KEY_NBR);
        fieldSetFlags()[0] = other.fieldSetFlags()[0];
      }
      if (isValidValue(fields()[1], other.CRTE_TMS)) {
        this.CRTE_TMS = data().deepCopy(fields()[1].schema(), other.CRTE_TMS);
        fieldSetFlags()[1] = other.fieldSetFlags()[1];
      }
      if (isValidValue(fields()[2], other.BIRTH_DT)) {
        this.BIRTH_DT = data().deepCopy(fields()[2].schema(), other.BIRTH_DT);
        fieldSetFlags()[2] = other.fieldSetFlags()[2];
      }
      if (isValidValue(fields()[3], other.BAL_AMT)) {
        this.BAL_AMT = data().deepCopy(fields()[3].schema(), other.BAL_AMT);
        fieldSetFlags()[3] = other.fieldSetFlags()[3];
      }
    }

    private Builder(com.original.User other) {
      super(SCHEMA$);
      if (isValidValue(fields()[0], other.KEY_NBR)) {
        this.KEY_NBR = data().deepCopy(fields()[0].schema(), other.KEY_NBR);
        fieldSetFlags()[0] = true;
      }
      if (isValidValue(fields()[1], other.CRTE_TMS)) {
        this.CRTE_TMS = data().deepCopy(fields()[1].schema(), other.CRTE_TMS);
        fieldSetFlags()[1] = true;
      }
      if (isValidValue(fields()[2], other.BIRTH_DT)) {
        this.BIRTH_DT = data().deepCopy(fields()[2].schema(), other.BIRTH_DT);
        fieldSetFlags()[2] = true;
      }
      if (isValidValue(fields()[3], other.BAL_AMT)) {
        this.BAL_AMT = data().deepCopy(fields()[3].schema(), other.BAL_AMT);
        fieldSetFlags()[3] = true;
      }
    }

    public java.lang.CharSequence getKEYNBR() {
      return KEY_NBR;
    }

    public com.original.User.Builder setKEYNBR(java.lang.CharSequence value) {
      validate(fields()[0], value);
      this.KEY_NBR = value;
      fieldSetFlags()[0] = true;
      return this;
    }

    public boolean hasKEYNBR() {
      return fieldSetFlags()[0];
    }


    public com.original.User.Builder clearKEYNBR() {
      KEY_NBR = null;
      fieldSetFlags()[0] = false;
      return this;
    }

    /**
      * Gets the value of the 'CRTE_TMS' field.
      * @return The value.
      */
    public java.time.Instant getCRTETMS() {
      return CRTE_TMS;
    }


    public com.original.User.Builder setCRTETMS(java.time.Instant value) {
      validate(fields()[1], value);
      this.CRTE_TMS = value;
      fieldSetFlags()[1] = true;
      return this;
    }

    /**
      * Checks whether the 'CRTE_TMS' field has been set.
      * @return True if the 'CRTE_TMS' field has been set, false otherwise.
      */
    public boolean hasCRTETMS() {
      return fieldSetFlags()[1];
    }


    public com.original.User.Builder clearCRTETMS() {
      CRTE_TMS = null;
      fieldSetFlags()[1] = false;
      return this;
    }

  
    public java.time.LocalDate getBIRTHDT() {
      return BIRTH_DT;
    }

    public com.original.User.Builder setBIRTHDT(java.time.LocalDate value) {
      validate(fields()[2], value);
      this.BIRTH_DT = value;
      fieldSetFlags()[2] = true;
      return this;
  
    public boolean hasBIRTHDT() {
      return fieldSetFlags()[2];
    }


    /**
      * Clears the value of the 'BIRTH_DT' field.
      * @return This builder.
      */
    public com.original.User.Builder clearBIRTHDT() {
      BIRTH_DT = null;
      fieldSetFlags()[2] = false;
      return this;
    }

    /**
      * Gets the value of the 'BAL_AMT' field.
      * @return The value.
      */
    public java.nio.ByteBuffer getBALAMT() {
      return BAL_AMT;
    }


    public com.original.User.Builder setBALAMT(java.nio.ByteBuffer value) {
      validate(fields()[3], value);
      this.BAL_AMT = value;
      fieldSetFlags()[3] = true;
      return this;
    }

    /**
      * Checks whether the 'BAL_AMT' field has been set.
      * @return True if the 'BAL_AMT' field has been set, false otherwise.
      */
    public boolean hasBALAMT() {
      return fieldSetFlags()[3];
    }


    public com.original.User.Builder clearBALAMT() {
      BAL_AMT = null;
      fieldSetFlags()[3] = false;
      return this;
    }

    @Override
    @SuppressWarnings("unchecked")
    public User build() {
      try {
        User record = new User();
        record.KEY_NBR = fieldSetFlags()[0] ? this.KEY_NBR : (java.lang.CharSequence) defaultValue(fields()[0]);
        record.CRTE_TMS = fieldSetFlags()[1] ? this.CRTE_TMS : (java.time.Instant) defaultValue(fields()[1]);
        record.BIRTH_DT = fieldSetFlags()[2] ? this.BIRTH_DT : (java.time.LocalDate) defaultValue(fields()[2]);
        record.BAL_AMT = fieldSetFlags()[3] ? this.BAL_AMT : (java.nio.ByteBuffer) defaultValue(fields()[3]);
        return record;
      } catch (org.apache.avro.AvroMissingFieldException e) {
        throw e;
      } catch (java.lang.Exception e) {
        throw new org.apache.avro.AvroRuntimeException(e);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private static final org.apache.avro.io.DatumWriter<User>
    WRITER$ = (org.apache.avro.io.DatumWriter<User>)MODEL$.createDatumWriter(SCHEMA$);

  @Override public void writeExternal(java.io.ObjectOutput out)
    throws java.io.IOException {
    WRITER$.write(this, SpecificData.getEncoder(out));
  }

  @SuppressWarnings("unchecked")
  private static final org.apache.avro.io.DatumReader<User>
    READER$ = (org.apache.avro.io.DatumReader<User>)MODEL$.createDatumReader(SCHEMA$);

  @Override public void readExternal(java.io.ObjectInput in)
    throws java.io.IOException {
    READER$.read(this, SpecificData.getDecoder(in));
  }
}

如果我修改了原始模式并从逻辑类型字段中取出空值。 Avro会为逻辑类型字段生成正确的conversions(选中突出显示的部分),并且我能够成功将Avro消息反序列化为POJO。

modified.avsc

{
    "type": "record",
    "name": "UserRecord",
    "namespace": "com.modified",
    "fields": [
        {
            "name": "user",
            "type": {
                "type": "record",
                "name": "User",
                "fields": [
                    {
                        "name": "KEY_NBR",
                        "type": [
                            "null",
                            "string"
                        ],
                        "default": null
                    },
                    {
                        "name": "CRTE_TMS",
                        "type":
                            {
                                "type": "long",
                                "logicalType": "timestamp-micros"
                            },
                        "default": 1
                    },
                    {
                        "name": "BIRTH_DT",
                        "type":
                            {
                                "type": "int",
                                "logicalType": "date"
                            },
                        "default": 1
                    },
                    {
                        "name": "BAL_AMT",
                        "type":
                            {
                                "type": "bytes",
                                "precision": 7,
                                "scale": 2,
                                "logicalType": "decimal"
                            }
                    }
                ]
            }
        },
        {
            "name": "beforeUser",
            "type": [
                "null",
                "User"
            ],
            "default": null
        }
    ]
}

通过修改后的模式生成的POJO类


package com.modified;

import org.apache.avro.generic.GenericArray;
import org.apache.avro.specific.SpecificData;
import org.apache.avro.util.Utf8;
import org.apache.avro.message.BinaryMessageEncoder;
import org.apache.avro.message.BinaryMessageDecoder;
import org.apache.avro.message.SchemaStore;

@org.apache.avro.specific.AvroGenerated
public class User extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
  private static final long serialVersionUID = -2671150121474031299L;
  public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"User\",\"namespace\":\"com.modified\",\"fields\":[{\"name\":\"KEY_NBR\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"CRTE_TMS\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-micros\"},\"default\":1},{\"name\":\"BIRTH_DT\",\"type\":{\"type\":\"int\",\"logicalType\":\"date\"},\"default\":1},{\"name\":\"BAL_AMT\",\"type\":{\"type\":\"bytes\",\"precision\":7,\"scale\":2,\"logicalType\":\"decimal\"}}]}");
  public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }

  private static SpecificData MODEL$ = new SpecificData();
static {
    MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.DateConversion());
    MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.TimestampMillisConversion());
  }

  private static final BinaryMessageEncoder<User> ENCODER =
      new BinaryMessageEncoder<User>(MODEL$, SCHEMA$);

  private static final BinaryMessageDecoder<User> DECODER =
      new BinaryMessageDecoder<User>(MODEL$, SCHEMA$);

  public static BinaryMessageEncoder<User> getEncoder() {
    return ENCODER;
  }

  public static BinaryMessageDecoder<User> getDecoder() {
    return DECODER;
  }

 
  public static BinaryMessageDecoder<User> createDecoder(SchemaStore resolver) {
    return new BinaryMessageDecoder<User>(MODEL$, SCHEMA$, resolver);
  }

 
  public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
    return ENCODER.encode(this);
  }

 
  public static User fromByteBuffer(
      java.nio.ByteBuffer b) throws java.io.IOException {
    return DECODER.decode(b);
  }

   private java.lang.CharSequence KEY_NBR;
   private java.time.Instant CRTE_TMS;
   private java.time.LocalDate BIRTH_DT;
   private java.nio.ByteBuffer BAL_AMT;

  public User() {}

 
  public User(java.lang.CharSequence KEY_NBR, java.time.Instant CRTE_TMS, java.time.LocalDate BIRTH_DT, java.nio.ByteBuffer BAL_AMT) {
    this.KEY_NBR = KEY_NBR;
    this.CRTE_TMS = CRTE_TMS.truncatedTo(java.time.temporal.ChronoUnit.MICROS);
    this.BIRTH_DT = BIRTH_DT;
    this.BAL_AMT = BAL_AMT;
  }

  public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; }
  public org.apache.avro.Schema getSchema() { return SCHEMA$; }
  // Used by DatumWriter.  Applications should not call.
  public java.lang.Object get(int field$) {
    switch (field$) {
    case 0: return KEY_NBR;
    case 1: return CRTE_TMS;
    case 2: return BIRTH_DT;
    case 3: return BAL_AMT;
    default: throw new org.apache.avro.AvroRuntimeException("Bad index");
    }
  }
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// This was missing from original POJO class thus failing deserialization

  private static final org.apache.avro.Conversion<?>[] conversions =
      new org.apache.avro.Conversion<?>[] {
      null,
      new org.apache.avro.data.TimeConversions.TimestampMicrosConversion(),
      new org.apache.avro.data.TimeConversions.DateConversion(),
      null,
      null
  };
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


  @Override
  public org.apache.avro.Conversion<?> getConversion(int field) {
    return conversions[field];
  }

  // Used by DatumReader.  Applications should not call.
  @SuppressWarnings(value="unchecked")
  public void put(int field$, java.lang.Object value$) {
    switch (field$) {
    case 0: KEY_NBR = (java.lang.CharSequence)value$; break;
    case 1: CRTE_TMS = (java.time.Instant)value$; break;
    case 2: BIRTH_DT = (java.time.LocalDate)value$; break;
    case 3: BAL_AMT = (java.nio.ByteBuffer)value$; break;
    default: throw new org.apache.avro.AvroRuntimeException("Bad index");
    }
  }

 
  public java.lang.CharSequence getKEYNBR() {
    return KEY_NBR;
  }


  public void setKEYNBR(java.lang.CharSequence value) {
    this.KEY_NBR = value;
  }

  
  public java.time.Instant getCRTETMS() {
    return CRTE_TMS;
  }


  public void setCRTETMS(java.time.Instant value) {
    this.CRTE_TMS = value.truncatedTo(java.time.temporal.ChronoUnit.MICROS);
  }


  public java.time.LocalDate getBIRTHDT() {
    return BIRTH_DT;
  }



  public void setBIRTHDT(java.time.LocalDate value) {
    this.BIRTH_DT = value;
  }

 
  public java.nio.ByteBuffer getBALAMT() {
    return BAL_AMT;
  }


  public void setBALAMT(java.nio.ByteBuffer value) {
    this.BAL_AMT = value;
  }

 
  public static com.modified.User.Builder newBuilder() {
    return new com.modified.User.Builder();
  }


  public static com.modified.User.Builder newBuilder(com.modified.User.Builder other) {
    if (other == null) {
      return new com.modified.User.Builder();
    } else {
      return new com.modified.User.Builder(other);
    }
  }

  
  public static com.modified.User.Builder newBuilder(com.modified.User other) {
    if (other == null) {
      return new com.modified.User.Builder();
    } else {
      return new com.modified.User.Builder(other);
    }
  }
 
  @org.apache.avro.specific.AvroGenerated
  public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<User>
    implements org.apache.avro.data.RecordBuilder<User> {

    private java.lang.CharSequence KEY_NBR;
    private java.time.Instant CRTE_TMS;
    private java.time.LocalDate BIRTH_DT;
    private java.nio.ByteBuffer BAL_AMT;

    /** Creates a new Builder */
    private Builder() {
      super(SCHEMA$);
    }

    private Builder(com.modified.User.Builder other) {
      super(other);
      if (isValidValue(fields()[0], other.KEY_NBR)) {
        this.KEY_NBR = data().deepCopy(fields()[0].schema(), other.KEY_NBR);
        fieldSetFlags()[0] = other.fieldSetFlags()[0];
      }
      if (isValidValue(fields()[1], other.CRTE_TMS)) {
        this.CRTE_TMS = data().deepCopy(fields()[1].schema(), other.CRTE_TMS);
        fieldSetFlags()[1] = other.fieldSetFlags()[1];
      }
      if (isValidValue(fields()[2], other.BIRTH_DT)) {
        this.BIRTH_DT = data().deepCopy(fields()[2].schema(), other.BIRTH_DT);
        fieldSetFlags()[2] = other.fieldSetFlags()[2];
      }
      if (isValidValue(fields()[3], other.BAL_AMT)) {
        this.BAL_AMT = data().deepCopy(fields()[3].schema(), other.BAL_AMT);
        fieldSetFlags()[3] = other.fieldSetFlags()[3];
      }
    }

    /**
     * Creates a Builder by copying an existing User instance
     * @param other The existing instance to copy.
     */
    private Builder(com.modified.User other) {
      super(SCHEMA$);
      if (isValidValue(fields()[0], other.KEY_NBR)) {
        this.KEY_NBR = data().deepCopy(fields()[0].schema(), other.KEY_NBR);
        fieldSetFlags()[0] = true;
      }
      if (isValidValue(fields()[1], other.CRTE_TMS)) {
        this.CRTE_TMS = data().deepCopy(fields()[1].schema(), other.CRTE_TMS);
        fieldSetFlags()[1] = true;
      }
      if (isValidValue(fields()[2], other.BIRTH_DT)) {
        this.BIRTH_DT = data().deepCopy(fields()[2].schema(), other.BIRTH_DT);
        fieldSetFlags()[2] = true;
      }
      if (isValidValue(fields()[3], other.BAL_AMT)) {
        this.BAL_AMT = data().deepCopy(fields()[3].schema(), other.BAL_AMT);
        fieldSetFlags()[3] = true;
      }
    }

    /**
      * Gets the value of the 'KEY_NBR' field.
      * @return The value.
      */
    public java.lang.CharSequence getKEYNBR() {
      return KEY_NBR;
    }


    public com.modified.User.Builder setKEYNBR(java.lang.CharSequence value) {
      validate(fields()[0], value);
      this.KEY_NBR = value;
      fieldSetFlags()[0] = true;
      return this;
    }

   
    public boolean hasKEYNBR() {
      return fieldSetFlags()[0];
    }


    public com.modified.User.Builder clearKEYNBR() {
      KEY_NBR = null;
      fieldSetFlags()[0] = false;
      return this;
    }

  
    public java.time.Instant getCRTETMS() {
      return CRTE_TMS;
    }


    public com.modified.User.Builder setCRTETMS(java.time.Instant value) {
      validate(fields()[1], value);
      this.CRTE_TMS = value.truncatedTo(java.time.temporal.ChronoUnit.MICROS);
      fieldSetFlags()[1] = true;
      return this;
    }


    public boolean hasCRTETMS() {
      return fieldSetFlags()[1];
    }


 
    public com.modified.User.Builder clearCRTETMS() {
      fieldSetFlags()[1] = false;
      return this;
    }

  
    public java.time.LocalDate getBIRTHDT() {
      return BIRTH_DT;
    }

    public com.modified.User.Builder setBIRTHDT(java.time.LocalDate value) {
      validate(fields()[2], value);
      this.BIRTH_DT = value;
      fieldSetFlags()[2] = true;
      return this;
    }


    public boolean hasBIRTHDT() {
      return fieldSetFlags()[2];
    }


    public com.modified.User.Builder clearBIRTHDT() {
      fieldSetFlags()[2] = false;
      return this;
    }

    /**
      * Gets the value of the 'BAL_AMT' field.
      * @return The value.
      */
    public java.nio.ByteBuffer getBALAMT() {
      return BAL_AMT;
    }


    public com.modified.User.Builder setBALAMT(java.nio.ByteBuffer value) {
      validate(fields()[3], value);
      this.BAL_AMT = value;
      fieldSetFlags()[3] = true;
      return this;
    }

    /**
      * Checks whether the 'BAL_AMT' field has been set.
      * @return True if the 'BAL_AMT' field has been set, false otherwise.
      */
    public boolean hasBALAMT() {
      return fieldSetFlags()[3];
    }


    public com.modified.User.Builder clearBALAMT() {
      BAL_AMT = null;
      fieldSetFlags()[3] = false;
      return this;
    }

    @Override
    @SuppressWarnings("unchecked")
    public User build() {
      try {
        User record = new User();
        record.KEY_NBR = fieldSetFlags()[0] ? this.KEY_NBR : (java.lang.CharSequence) defaultValue(fields()[0]);
        record.CRTE_TMS = fieldSetFlags()[1] ? this.CRTE_TMS : (java.time.Instant) defaultValue(fields()[1]);
        record.BIRTH_DT = fieldSetFlags()[2] ? this.BIRTH_DT : (java.time.LocalDate) defaultValue(fields()[2]);
        record.BAL_AMT = fieldSetFlags()[3] ? this.BAL_AMT : (java.nio.ByteBuffer) defaultValue(fields()[3]);
        return record;
      } catch (org.apache.avro.AvroMissingFieldException e) {
        throw e;
      } catch (java.lang.Exception e) {
        throw new org.apache.avro.AvroRuntimeException(e);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private static final org.apache.avro.io.DatumWriter<User>
    WRITER$ = (org.apache.avro.io.DatumWriter<User>)MODEL$.createDatumWriter(SCHEMA$);

  @Override public void writeExternal(java.io.ObjectOutput out)
    throws java.io.IOException {
    WRITER$.write(this, SpecificData.getEncoder(out));
  }

  @SuppressWarnings("unchecked")
  private static final org.apache.avro.io.DatumReader<User>
    READER$ = (org.apache.avro.io.DatumReader<User>)MODEL$.createDatumReader(SCHEMA$);

  @Override public void readExternal(java.io.ObjectInput in)
    throws java.io.IOException {
    READER$.read(this, SpecificData.getDecoder(in));
  }
}

我不明白为什么Avro会这样做,并且我不满意破解原始模式以使事情正常进行。 有人可以帮我弄清楚世界如何处理这些问题?

注意:该问题与我所问的this个原始问题有关。

1 个答案:

答案 0 :(得分:2)

我在 avro 1.10.1 版中得到的相同行为。根据我的经验,Avro 无法对 null 和其中一种逻辑类型(时间戳、十进制)的联合类型字段进行反实现

示例:

<块引用>

{ "name": "成本金额", “类型”: [ “空值”, { “类型”:“字节”, "logicalType": "十进制", “精度”:19, “规模”:2 } ], "default": null },