Skip to content

Instantly share code, notes, and snippets.

@amard33p
Created October 18, 2024 06:40
Show Gist options
  • Save amard33p/a6d170d6fd24e97965502d6b1a93c4ed to your computer and use it in GitHub Desktop.
Save amard33p/a6d170d6fd24e97965502d6b1a93c4ed to your computer and use it in GitHub Desktop.
Avro to Json Serialization and Deserialization Utility which shows field name in exception message
package com.example.jsontoavro;
import org.apache.avro.AvroTypeException;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.EncoderFactory;
import org.apache.avro.io.JsonDecoder;
import org.apache.avro.io.JsonEncoder;
import org.apache.avro.io.ResolvingDecoder;
import org.apache.avro.specific.SpecificDatumReader;
import org.apache.avro.specific.SpecificDatumWriter;
import org.apache.avro.specific.SpecificRecordBase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public final class AvroUtil {
private AvroUtil() {
}
/***
* Converts an Avro specific record to a JSON string.
*
* @param specificRecord The Avro specific record.
* @param <T> The type of the Avro specific record. It must be a subclass of SpecificRecordBase.
* @return A string with a JSON representation of the input.
*/
public static <T extends SpecificRecordBase> String toJsonString(T specificRecord) {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
DatumWriter<T> writer = new SpecificDatumWriter<>(specificRecord.getSchema());
JsonEncoder encoder = EncoderFactory.get().jsonEncoder(specificRecord.getSchema(), out, true);
writer.write(specificRecord, encoder);
encoder.flush();
return out.toString();
} catch (Exception e) {
throw new IllegalStateException("Failed to read SpecificRecordBase", e);
}
}
/***
* Parses a JSON string and returns an Avro specific record, provided a schema.
*
* @param schema The Avro schema.
* @param json A JSON representation of the object.
* @return The Avro specific record.
*/
public static GenericRecord parseFromJsonString(Schema schema, String json) throws IllegalStateException {
{
try {
DatumReader<GenericRecord> reader = new CustomSpecificDatumReader<>(schema);
JsonDecoder decoder = DecoderFactory.get().jsonDecoder(schema, json);
return reader.read(null, decoder);
} catch (Exception e) {
throw new IllegalStateException("Failed to parse json.", e);
}
}
}
}
class CustomSpecificDatumReader<T> extends SpecificDatumReader<T> {
public CustomSpecificDatumReader(Schema schema) {
super(schema);
}
/*
* Shows the field name and the expected type in the exception message.
*/
@Override
protected void readField(Object record, Schema.Field field, Object oldDatum, ResolvingDecoder in, Object state) throws IOException {
try {
super.readField(record, field, oldDatum, in, state);
} catch (AvroTypeException ex) {
throw new AvroTypeException("Error reading field '" + field.name() + "' of type " + field.schema().getType() + ": " + ex.getMessage());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment