Skip to content

Commit

Permalink
Parsing Avro bytes or fixed types with logical type decimal into …
Browse files Browse the repository at this point in the history
…java.util.BigDecimal.
  • Loading branch information
MichalFoksa committed Jan 2, 2025
1 parent 4dd9828 commit 1816c38
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import java.lang.annotation.Target;

/**
* Instructs the {@link com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaGenerator AvroSchemaGenerator}
* When generate logical types is enabled, annotation instructs the
* {@link com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaGenerator AvroSchemaGenerator}
* to declare the annotated property's logical type as "decimal" ({@link org.apache.avro.LogicalTypes.Decimal}).
* By default, the Avro type is "bytes" ({@link org.apache.avro.Schema.Type#BYTES}), unless the field is also
* annotated with {@link com.fasterxml.jackson.dataformat.avro.AvroFixedSize}, in which case the Avro type
Expand All @@ -29,6 +30,6 @@
/**
* Scale must be zero or a positive integer less than or equal to the precision.
*/
int scale() default 0;
int scale();

}
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,34 @@ public long getRemainingElements()
public abstract int decodeIndex() throws IOException;
public abstract int decodeEnum() throws IOException;

/*
/**********************************************************
/* Methods for AvroReadContext implementations: decimals
/**********************************************************
*/

public JsonToken decodeBytesDecimal(int scale) throws IOException {
decodeBytes();
_numberBigDecimal = new BigDecimal(new BigInteger(_binaryValue), scale);
_numTypesValid = NR_BIGDECIMAL;
return JsonToken.VALUE_NUMBER_FLOAT;
}

public void skipBytesDecimal() throws IOException {
skipBytes();
}

public JsonToken decodeFixedDecimal(int scale, int size) throws IOException {
decodeFixed(size);
_numberBigDecimal = new BigDecimal(new BigInteger(_binaryValue), scale);
_numTypesValid = NR_BIGDECIMAL;
return JsonToken.VALUE_NUMBER_FLOAT;
}

public void skipFixedDecimal(int size) throws IOException {
skipFixed(size);
}

/*
/**********************************************************
/* Methods for AvroReadContext impls, other
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.util.*;

import org.apache.avro.LogicalTypes;
import org.apache.avro.Schema;

import com.fasterxml.jackson.dataformat.avro.deser.ScalarDecoder.*;
Expand Down Expand Up @@ -56,12 +57,18 @@ public ScalarDecoder createScalarValueDecoder(Schema type)
case BOOLEAN:
return READER_BOOLEAN;
case BYTES:
if (type.getLogicalType() instanceof LogicalTypes.Decimal) {
return new BytesDecimalReader(((LogicalTypes.Decimal) type.getLogicalType()).getScale());
}
return READER_BYTES;
case DOUBLE:
return READER_DOUBLE;
case ENUM:
return new EnumDecoder(AvroSchemaHelper.getFullName(type), type.getEnumSymbols());
case FIXED:
if (type.getLogicalType() instanceof LogicalTypes.Decimal) {
return new FixedDecimalReader(((LogicalTypes.Decimal) type.getLogicalType()).getScale(), type.getFixedSize());
}
return new FixedDecoder(type.getFixedSize(), AvroSchemaHelper.getFullName(type));
case FLOAT:
return READER_FLOAT;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.fasterxml.jackson.dataformat.avro.deser;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;

import com.fasterxml.jackson.core.JsonToken;
Expand Down Expand Up @@ -546,4 +547,100 @@ public void skipValue(AvroParserImpl parser) throws IOException {
}
}
}

protected final static class FixedDecimalReader extends ScalarDecoder {
private final int _scale;
private final int _size;

public FixedDecimalReader(int scale, int size) {
_scale = scale;
_size = size;
}

@Override
public JsonToken decodeValue(AvroParserImpl parser) throws IOException {
return parser.decodeFixedDecimal(_scale, _size);
}

@Override
protected void skipValue(AvroParserImpl parser) throws IOException {
parser.skipFixedDecimal(_size);
}

@Override
public String getTypeId() {
return AvroSchemaHelper.getTypeId(BigDecimal.class);
}

@Override
public AvroFieldReader asFieldReader(String name, boolean skipper) {
return new FR(name, skipper, getTypeId(), _scale, _size);
}

private final static class FR extends AvroFieldReader {
private final int _scale;
private final int _size;
public FR(String name, boolean skipper, String typeId, int scale, int size) {
super(name, skipper, typeId);
_scale = scale;
_size = size;
}

@Override
public JsonToken readValue(AvroReadContext parent, AvroParserImpl parser) throws IOException {
return parser.decodeFixedDecimal(_scale, _size);
}

@Override
public void skipValue(AvroParserImpl parser) throws IOException {
parser.skipFixedDecimal(_size);
}
}
}

protected final static class BytesDecimalReader extends ScalarDecoder {
private final int _scale;

public BytesDecimalReader(int scale) {
_scale = scale;
}

@Override
public JsonToken decodeValue(AvroParserImpl parser) throws IOException {
return parser.decodeBytesDecimal(_scale);
}

@Override
protected void skipValue(AvroParserImpl parser) throws IOException {
parser.skipBytesDecimal();
}

@Override
public String getTypeId() {
return AvroSchemaHelper.getTypeId(BigDecimal.class);
}

@Override
public AvroFieldReader asFieldReader(String name, boolean skipper) {
return new FR(name, skipper, getTypeId(), _scale);
}

private final static class FR extends AvroFieldReader {
private final int _scale;
public FR(String name, boolean skipper, String typeId, int scale) {
super(name, skipper, typeId);
_scale = scale;
}

@Override
public JsonToken readValue(AvroReadContext parent, AvroParserImpl parser) throws IOException {
return parser.decodeBytesDecimal(_scale);
}

@Override
public void skipValue(AvroParserImpl parser) throws IOException {
parser.skipFloat();
}
}
}
}
Loading

0 comments on commit 1816c38

Please sign in to comment.