package org.apache.avro.io; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.apache.avro.AvroTypeException; import org.apache.avro.Schema; import org.apache.avro.generic.GenericDatumReader; import org.apache.avro.generic.GenericDatumWriter; import org.apache.avro.generic.GenericRecord; import org.apache.avro.io.HackedJsonDecoder; import org.apache.avro.io.HackedJsonEncoder; import org.junit.Test; import org.apache.avro.io.schemas.Document; import org.apache.avro.io.schemas.PersonEntry; import org.apache.avro.io.schemas.PersonWithDocument; /** * @author Mateusz Kobos */ public class JsonCodersTest { @Test public void testJsonSchemaSimpleWithNull() throws IOException { checkJsonReadWrite("{\"age\":10}\n{\"age\":null}", getCertainPersonNullableSchema()); } @Test public void testJsonSchemaWithoutNull() throws IOException { checkJsonReadWrite("{\"age\":10}\n{\"age\":33}", getCertainPersonSchema()); } @Test public void testJsonSchemaWritingNullToNonNullableField() throws IOException { checkJsonReadWrite("{\"age\":10}\n{\"age\":null}", getCertainPersonSchema(), true); } @Test public void testSimpleNullableField() throws IOException { checkJsonReadWrite( "{\"id\":1,\"title\":\"Interesting stuff\",\"authorIds\":[1,2,3]}", Document.SCHEMA$); checkJsonReadWrite("{\"id\":1,\"title\":null,\"authorIds\":[1,2,3]}", Document.SCHEMA$); // checkJsonReadWrite("{\"id\":1,\"authorIds\":[1,2,3]}", Document.SCHEMA$); checkJsonReadWrite("{\"id\":1,\"title\":\"Interesting stuff\"}", Document.SCHEMA$, true); } @Test public void testNestedStructures() throws IOException { String records = readFromResources( "org/apache/avro/io/nested_data.json"); checkJsonReadWrite(records, PersonWithDocument.SCHEMA$); } /** * When null is not one of the elements in the union, you have to specify * the type of the elements in the union explicitly */ @Test public void testUnionWithoutNull() throws IOException { String records = readFromResources( "org/apache/avro/io/union_without_null.json"); checkJsonReadWrite(records, PersonEntry.SCHEMA$); checkJsonReadWrite("{\"id\":22,\"externalId\":3}", PersonEntry.SCHEMA$, true); } private static String readFromResources(String path) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(Thread .currentThread().getContextClassLoader() .getResourceAsStream(path))); StringBuffer buffer = new StringBuffer(); boolean isFirst = true; for (String line = reader.readLine(); line != null; line = reader .readLine()) { if (isFirst) { isFirst = false; } else { buffer.append("\n"); } isFirst = false; buffer.append(line); } return buffer.toString(); } private void checkJsonReadWrite(String jsonInput, Schema schema) throws IOException { checkJsonReadWrite(jsonInput, schema, false); } /** * Convert given JSON input string to Avro records, then convert it back to * JSON and compare it with the original. */ private void checkJsonReadWrite(String jsonInput, Schema schema, boolean shouldThrowParsingException) throws IOException { try { List<GenericRecord> outRecords = toRecords(jsonInput, schema, schema); String jsonOutput = toJson(outRecords, schema); assertEquals(jsonInput, jsonOutput); } catch (AvroTypeException ex) { if (shouldThrowParsingException) { return; } else { throw new RuntimeException(ex); } } if (shouldThrowParsingException) { assertTrue( "This code should not have been reached because of previous " + "exception thrown", false); } } private static Schema getCertainPersonSchema() throws IOException { return new Schema.Parser().parse(readFromResources( "org/apache/avro/io/schemas/certain_person.json")); } private static Schema getCertainPersonNullableSchema() throws IOException { return new Schema.Parser().parse(readFromResources( "org/apache/avro/io/schemas/certain_person_nullable.json")); } private static String toJson(List<GenericRecord> records, Schema schema) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); HackedJsonEncoder jsonEncoder = new HackedJsonEncoder(schema, output); GenericDatumWriter<GenericRecord> writer = new GenericDatumWriter<GenericRecord>(schema); for (GenericRecord record : records) { writer.write(record, jsonEncoder); } jsonEncoder.flush(); output.flush(); return output.toString(); } private static List<GenericRecord> toRecords(String inputJson, Schema writerSchema, Schema readerSchema) throws IOException { HackedJsonDecoder jsonDecoder = new HackedJsonDecoder(writerSchema, inputJson); GenericDatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>( writerSchema, readerSchema); List<GenericRecord> records = new ArrayList<GenericRecord>(); while (true) { try { GenericRecord record = reader.read(null, jsonDecoder); records.add(record); } catch (EOFException e) { break; } } return records; } }