Это что-то вроде удара в темноте, если кто-нибудь, кто разбирается в Java-реализации Apache Avro, читает это.
Моя цель высокого уровня - найти какой-то способ передачи некоторых серий данных avro.по сети (скажем, HTTP, например, но конкретный протокол не так важен для этой цели).В моем контексте у меня есть HttpServletResponse, в который мне нужно каким-то образом записать эти данные.
Сначала я попытался записать данные как виртуальную версию файла контейнера avro (предположим, что «response» имеет типHttpServletResponse):
response.setContentType("application/octet-stream");
response.setHeader("Content-transfer-encoding", "binary");
ServletOutputStream outStream = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(outStream);
Schema someSchema = Schema.parse(".....some valid avro schema....");
GenericRecord someRecord = new GenericData.Record(someSchema);
someRecord.put("somefield", someData);
...
GenericDatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(someSchema);
DataFileWriter<GenericRecord> fileWriter = new DataFileWriter<GenericRecord>(datumWriter);
fileWriter.create(someSchema, bos);
fileWriter.append(someRecord);
fileWriter.close();
bos.flush();
Все было хорошо и здорово, за исключением того, что оказалось, что Avro действительно не предоставляет способ чтения файла контейнера отдельно от реального файла: DataFileReader имеет только два конструктора:
public DataFileReader(File file, DatumReader<D> reader);
и
public DataFileReader(SeekableInput sin, DatumReader<D> reader);
, где SeekableInput - это настраиваемая форма, специфичная для avro, создание которой также заканчивается чтением из файла.Теперь, учитывая, что, если нет какого-либо способа каким-либо образом принудительно ввести InputStream в файл (/544143/sozdaite-obekt-java-file-ili-ekvivalentnyi-ispolzuya-baitovyi-massiv-v-pamyati-bez-fizicheskogo-faila предполагает, что это не так, и я также попытался просмотреть документацию по Java), этот подход не будет работать, если читатель надругой конец OutputStream получает этот файл контейнера avro (я не уверен, почему они позволили одному выводить файлы двоичного контейнера avro в произвольный OutputStream, не предоставляя способ прочитать их из соответствующего InputStream на другом конце, но это не так.точка).Похоже, что реализация программы чтения контейнерных файлов требует функции «поиска», которую предоставляет конкретный файл.
Хорошо, так что не похоже, что этот подход будет делать то, что я хочу.Как насчет создания ответа JSON, который имитирует файл контейнера avro?
public static Schema WRAPPER_SCHEMA = Schema.parse(
"{\"type\": \"record\", " +
"\"name\": \"AvroContainer\", " +
"\"doc\": \"a JSON avro container file\", " +
"\"namespace\": \"org.bar.foo\", " +
"\"fields\": [" +
"{\"name\": \"schema\", \"type\": \"string\", \"doc\": \"schema representing the included data\"}, " +
"{\"name\": \"data\", \"type\": \"bytes\", \"doc\": \"packet of data represented by the schema\"}]}"
);
Я не уверен, что это лучший способ приблизиться к этому, учитывая вышеуказанные ограничения, но похоже, что это может сработать,Я помещу схему (например, "Schema someSchema" сверху) как строку в поле "схема", а затем вставлю в серию avro-binary-serialized форму записи, соответствующей этой схеме (т. Е. "GenericRecordsomeRecord ") внутри поля" data ".
Я на самом деле хотел узнать о конкретной детали того, что описано ниже, но я подумал, что было бы также целесообразно дать больший контекст, так что еслиесть лучший высокоуровневый подход, который я мог бы использовать (этот подход работает, но он не кажется оптимальным), пожалуйста, дайте мне знать.
Мой вопрос в том, что я предполагаю, что я использую этот подход на основе JSON,как записать двоичное представление моей записи avro в поле «data» схемы AvroContainer?Например, я встал здесь:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GenericDatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(someSchema);
Encoder e = new BinaryEncoder(baos);
datumWriter.write(resultsRecord, e);
e.flush();
GenericRecord someRecord = new GenericData.Record(someSchema);
someRecord.put("schema", someSchema.toString());
someRecord.put("data", ByteBuffer.wrap(baos.toByteArray()));
datumWriter = new GenericDatumWriter<GenericRecord>(WRAPPER_SCHEMA);
JsonGenerator jsonGenerator = new JsonFactory().createJsonGenerator(baos, JsonEncoding.UTF8);
e = new JsonEncoder(WRAPPER_SCHEMA, jsonGenerator);
datumWriter.write(someRecord, e);
e.flush();
PrintWriter printWriter = response.getWriter(); // recall that response is the HttpServletResponse
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
printWriter.print(baos.toString("UTF-8"));
Сначала я попытался опустить предложение ByteBuffer.wrap, но затем строка
datumWriter.write(someRecord, e);
вызвала исключение, которое я не смогприведите массив байтов в ByteBuffer.Справедливо, похоже, что когда класс Encoder (из которых JsonEncoder является подклассом) вызывается для записи объекта Avro Bytes, он требует, чтобы в качестве аргумента был задан ByteBuffer.Таким образом, я попытался инкапсулировать byte [] с помощью java.nio.ByteBuffer.wrap, но когда данные были распечатаны, они были напечатаны в виде прямой серии байтов без прохождения через шестнадцатеричное представление avro:
"data": {"bytes": ".....some gibberish other than the expected format...}
Это не кажется правильным.Согласно документации avro, пример объекта байтов, который они дают, говорит, что мне нужно вставить объект json, пример которого выглядит как "\ u00FF", и то, что я вставил туда, явно не в этом формате.Теперь я хочу узнать следующее:
- Что является примером формата байтов avro?Это выглядит как "\ uDEADBEEFDEADBEEF ..."?
- Как мне привести мои двоичные avro-данные (как выводимые BinaryEncoder в массив byte []) в формат, который я могу вставить в объект GenericRecord и правильно ли он печатать в JSON?Например, я хочу Object DATA, для которого я могу вызвать некоторые GenericRecord "someRecord.put (" data ", DATA);"с моими сериализованными данными avro?
- Как бы я затем считал эти данные обратно в байтовый массив на другом (потребительском) конце, когда ему дается текстовое представление JSON, и он хочет воссоздать GenericRecord, как представленоJSON в формате AvroContainer?
- (повторяя вопрос ранее) Есть ли лучший способ сделать все это?