如何在转换为json时排除avro名称空间

时间:2016-08-26 09:29:44

标签: avro jackson-dataformat-avro

最近我正在编写用于将数据推送到elasticsearch的映射器。 我的输入是avro对象,我试图转换为Json。每件事都很好,但我在json中获得命名空间,其中elasticsearch不允许命名空间作为关键对象。

"requestobj":{"com.nw.data.Request":{"event_id":null,"event_epoch":-1,"event_dispatch_epoch":-1,"server_epoch":1471852915279,"date":{"string":"2016-08-22"},"time":{"string":"08:01:55"},"req_source":{"string":"app"},"req_channel":{"string":"Mobile"},"req_dimension":{"string":"1312x704"}

有没有办法排除命名空间 - com.nw.data.Request

我正在使用以下代码将avro转换为json:

public static String getJsonString(GenericRecord record) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        JsonEncoder encoder = EncoderFactory.get().jsonEncoder(record.getSchema(), os);
        DatumWriter<GenericRecord> writer = new GenericDatumWriter<GenericRecord>(record.getSchema());
        writer.setSchema(record.getSchema());
        writer.write(record, encoder);
        encoder.flush();
        String jsonString = new String(os.toByteArray(), Charset.forName("UTF-8"));
        os.close();

        return jsonString;
    }

2 个答案:

答案 0 :(得分:2)

您也可以覆盖JsonEncoder:

    import java.io.{ByteArrayOutputStream, IOException, OutputStream}
    import java.nio.ByteBuffer
    import java.util

    import org.apache.avro.generic.{GenericDatumReader, GenericDatumWriter}
    import org.apache.avro.io.{DecoderFactory, ParsingEncoder}
    import org.apache.avro.io.parsing.{JsonGrammarGenerator, Parser, Symbol}
    import org.apache.avro.util.Utf8
    import org.apache.avro.{AvroTypeException, Schema}
    import org.codehaus.jackson.util.{DefaultPrettyPrinter, MinimalPrettyPrinter}
    import org.codehaus.jackson.{JsonEncoding, JsonFactory, JsonGenerator}


    object inJson{

      private def toObject(binary: Array[Byte], schemaWriter: Schema)(schemaReader : Schema = schemaWriter) : Object = {
        val decoder = DecoderFactory.get().binaryDecoder(binary, null)
        val reader = new GenericDatumReader[Object](schemaWriter, schemaReader)
        reader.read(null, decoder)
      }

      private def toJson(`object`: Object,schemaWriter: Schema): String = {
        val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream()
        try {
          val encoder = new JsonEncoder(schemaWriter, outputStream, true)
          val writer = new GenericDatumWriter[Object](schemaWriter)
          writer.write(`object`, encoder)
          encoder.flush()
          outputStream.toString
        } finally {
          outputStream.close()
        }
      }

      def ~>(binary: Array[Byte], schemaWriter: Schema, schemaReader : Schema, schemaJson : Schema) : String = {
        Option(toObject(binary, schemaWriter)(schemaReader)).map(toJson(_, schemaJson)).getOrElse("")
      }

      def ~>(`object`: Object, schemaWriter: Schema, schemaReader : Schema, schemaJson : Schema) : String = {
        toJson(`object`, schemaJson)
      }
    }

    object JsonEncoder{
      private val LINE_SEPARATOR: String = System.getProperty("line.separator")

      // by default, one object per line.
      // with pretty option use default pretty printer with root line separator.
      @throws[IOException]
      private def getJsonGenerator(out: OutputStream, pretty: Boolean): JsonGenerator = {
        if (null == out) throw new NullPointerException("OutputStream cannot be null")
        val g: JsonGenerator = new JsonFactory().createJsonGenerator(out, JsonEncoding.UTF8)
        if (pretty) {
          val pp: DefaultPrettyPrinter = new DefaultPrettyPrinter() { //@Override
            @throws[IOException]
            override def writeRootValueSeparator(jg: JsonGenerator): Unit = {
           //   jg.writeRaw(LINE_SEPARATOR)
            }
          }
          g.setPrettyPrinter(pp)
        }
        else {
          val pp: MinimalPrettyPrinter = new MinimalPrettyPrinter
          pp.setRootValueSeparator(LINE_SEPARATOR)
          g.setPrettyPrinter(pp)
        }
        g
      }
    }

    class JsonEncoder(schema : Schema, out: JsonGenerator) extends ParsingEncoder with Parser.ActionHandler{

      val parser = new Parser(new JsonGrammarGenerator().generate(schema), this)
      /**
        * Has anything been written into the collections?
        */
      protected var isEmpty: util.BitSet = new util.BitSet

      def this(sc: Schema, out: OutputStream) {
        this(sc, JsonEncoder.getJsonGenerator(out, false))
      }

      def this(sc: Schema, out: OutputStream, pretty: Boolean) {
        this(sc, JsonEncoder.getJsonGenerator(out, pretty))
      }


      @throws[IOException]
      override def writeIndex(unionIndex: Int): Unit = {
        parser.advance(Symbol.UNION)
        val top: Symbol.Alternative = parser.popSymbol.asInstanceOf[Symbol.Alternative]
        val symbol: Symbol = top.getSymbol(unionIndex)
        parser.pushSymbol(symbol)
      }

      @throws[IOException]
      override def flush(): Unit = {
        parser.processImplicitActions()
        if (out != null) out.flush()
      }

      @throws[IOException]
      override def writeNull(): Unit = {
        parser.advance(Symbol.NULL)
        out.writeNull()
      }

      @throws[IOException]
      override def writeBoolean(b: Boolean): Unit = {
        parser.advance(Symbol.BOOLEAN)
        out.writeBoolean(b)
      }

      @throws[IOException]
      override def writeInt(n: Int): Unit = {
        parser.advance(Symbol.INT)
        out.writeNumber(n)
      }

      @throws[IOException]
      override def writeLong(n: Long): Unit = {
        parser.advance(Symbol.LONG)
        out.writeNumber(n)
      }

      @throws[IOException]
      override def writeFloat(f: Float): Unit = {
        parser.advance(Symbol.FLOAT)
        out.writeNumber(f)
      }

      @throws[IOException]
      override def writeDouble(d: Double): Unit = {
        parser.advance(Symbol.DOUBLE)
        out.writeNumber(d)
      }

      @throws[IOException]
      override def writeString(utf8: Utf8): Unit = {
        writeString(utf8.toString)
      }

      @throws[IOException]
      override def writeString(str: String): Unit = {
        parser.advance(Symbol.STRING)
        if (parser.topSymbol eq Symbol.MAP_KEY_MARKER) {
          parser.advance(Symbol.MAP_KEY_MARKER)
          out.writeFieldName(str)
        }
        else out.writeString(str)
      }

      @throws[IOException]
      override def writeBytes(bytes: ByteBuffer): Unit = {
        if (bytes.hasArray) writeBytes(bytes.array, bytes.position, bytes.remaining)
        else {
          val b: Array[Byte] = new Array[Byte](bytes.remaining)
          bytes.duplicate.get(b)
          writeBytes(b)
        }
      }

      @throws[IOException]
      override def writeBytes(bytes: Array[Byte], start: Int, len: Int): Unit = {
        parser.advance(Symbol.BYTES)
        writeByteArray(bytes, start, len)
      }

      @throws[IOException]
      private def writeByteArray(bytes: Array[Byte], start: Int, len: Int): Unit = {
        out.writeString(new String(bytes, start, len, "ISO-8859-1"))
      }

      @throws[IOException]
      override def writeFixed(bytes: Array[Byte], start: Int, len: Int): Unit = {
        parser.advance(Symbol.FIXED)
        val top: Symbol.IntCheckAction = parser.popSymbol.asInstanceOf[Symbol.IntCheckAction]
        if (len != top.size) throw new AvroTypeException("Incorrect length for fixed binary: expected " + top.size + " but received " + len + " bytes.")
        writeByteArray(bytes, start, len)
      }

      @throws[IOException]
      override def writeEnum(e: Int): Unit = {
        parser.advance(Symbol.ENUM)
        val top: Symbol.EnumLabelsAction = parser.popSymbol.asInstanceOf[Symbol.EnumLabelsAction]
        if (e < 0 || e >= top.size) throw new AvroTypeException("Enumeration out of range: max is " + top.size + " but received " + e)
        out.writeString(top.getLabel(e))
      }

      @throws[IOException]
      override def writeArrayStart(): Unit = {
        parser.advance(Symbol.ARRAY_START)
        out.writeStartArray()
        push()
        isEmpty.set(depth)
      }

      @throws[IOException]
      override def writeArrayEnd(): Unit = {
        if (!isEmpty.get(pos)) parser.advance(Symbol.ITEM_END)
        pop()
        parser.advance(Symbol.ARRAY_END)
        out.writeEndArray()
      }

      @throws[IOException]
      override def writeMapStart(): Unit = {
        push()
        isEmpty.set(depth)
        parser.advance(Symbol.MAP_START)
        out.writeStartObject()
      }

      @throws[IOException]
      override def writeMapEnd(): Unit = {
        if (!isEmpty.get(pos)) parser.advance(Symbol.ITEM_END)
        pop()
        parser.advance(Symbol.MAP_END)
        out.writeEndObject()
      }

      @throws[IOException]
      override def startItem(): Unit = {
        if (!isEmpty.get(pos)) parser.advance(Symbol.ITEM_END)
        super.startItem()
        isEmpty.clear(depth)
      }



      @throws[IOException]
      override def doAction(input: Symbol, top: Symbol): Symbol = {
        if (top.isInstanceOf[Symbol.FieldAdjustAction]) {
          val fa: Symbol.FieldAdjustAction = top.asInstanceOf[Symbol.FieldAdjustAction]
          out.writeFieldName(fa.fname)
        }
        else if (top eq Symbol.RECORD_START) out.writeStartObject()
        else if ((top eq Symbol.RECORD_END) || (top eq Symbol.UNION_END)) out.writeEndObject()
        else if (top ne Symbol.FIELD_END) throw new AvroTypeException("Unknown action symbol " + top)
        null
      }
    }

答案 1 :(得分:0)

就像斯特凡提到的那样,到目前为止,唯一的方法就是创建自己的JsonEncoder。有趣的是,您真正需要更改的唯一方法是writeIndex,当它不同于NULL时将追加联合记录。这是我的代码:

public class CustomJsonEncoder extends ParsingEncoder implements Parser.ActionHandler {
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    final Parser parser;
    private JsonGenerator out;
    protected BitSet isEmpty = new BitSet();

    public CustomJsonEncoder(Schema sc, OutputStream out) throws IOException {
        this(sc, getJsonGenerator(out, false));
    }

    public CustomJsonEncoder(Schema sc, OutputStream out, boolean pretty) throws IOException {
        this(sc, getJsonGenerator(out, pretty));
    }

    public CustomJsonEncoder(Schema sc, JsonGenerator out) throws IOException {
        configure(out);
        this.parser = new Parser(new JsonGrammarGenerator().generate(sc), this);
    }

    @Override
    public void flush() throws IOException {
        parser.processImplicitActions();
        if (out != null) {
            out.flush();
        }
    }

    private static JsonGenerator getJsonGenerator(OutputStream out, boolean pretty) throws IOException {
        if (null == out)
            throw new NullPointerException("OutputStream cannot be null");
        JsonGenerator g = new JsonFactory().createGenerator(out, JsonEncoding.UTF8);
        if (pretty) {
            DefaultPrettyPrinter pp = new DefaultPrettyPrinter() {
                @Override
                public void writeRootValueSeparator(JsonGenerator jg) throws IOException {
                    jg.writeRaw(LINE_SEPARATOR);
                }
            };
            g.setPrettyPrinter(pp);
        } else {
            MinimalPrettyPrinter pp = new MinimalPrettyPrinter();
            pp.setRootValueSeparator(LINE_SEPARATOR);
            g.setPrettyPrinter(pp);
        }
        return g;
    }

    public CustomJsonEncoder configure(OutputStream out) throws IOException {
        this.configure(getJsonGenerator(out, false));
        return this;
    }

    private CustomJsonEncoder configure(JsonGenerator generator) throws IOException {
        if (null == generator)
            throw new NullPointerException("JsonGenerator cannot be null");
        if (null != parser) {
            flush();
        }
        this.out = generator;
        return this;
    }

    @Override
    public void writeNull() throws IOException {
        parser.advance(Symbol.NULL);
        out.writeNull();
    }

    @Override
    public void writeBoolean(boolean b) throws IOException {
        parser.advance(Symbol.BOOLEAN);
        out.writeBoolean(b);
    }

    @Override
    public void writeInt(int n) throws IOException {
        parser.advance(Symbol.INT);
        out.writeNumber(n);
    }

    @Override
    public void writeLong(long n) throws IOException {
        parser.advance(Symbol.LONG);
        out.writeNumber(n);
    }

    @Override
    public void writeFloat(float f) throws IOException {
        parser.advance(Symbol.FLOAT);
        out.writeNumber(f);
    }

    @Override
    public void writeDouble(double d) throws IOException {
        parser.advance(Symbol.DOUBLE);
        out.writeNumber(d);
    }

    @Override
    public void writeString(Utf8 utf8) throws IOException {
        writeString(utf8.toString());
    }

    @Override
    public void writeString(String str) throws IOException {
        parser.advance(Symbol.STRING);
        if (parser.topSymbol() == Symbol.MAP_KEY_MARKER) {
            parser.advance(Symbol.MAP_KEY_MARKER);
            out.writeFieldName(str);
        } else {
            out.writeString(str);
        }
    }

    @Override
    public void writeBytes(ByteBuffer bytes) throws IOException {
        if (bytes.hasArray()) {
            writeBytes(bytes.array(), bytes.position(), bytes.remaining());
        } else {
            byte[] b = new byte[bytes.remaining()];
            bytes.duplicate().get(b);
            writeBytes(b);
        }
    }

    @Override
    public void writeBytes(byte[] bytes, int start, int len) throws IOException {
        parser.advance(Symbol.BYTES);
        writeByteArray(bytes, start, len);
    }

    private void writeByteArray(byte[] bytes, int start, int len) throws IOException {
        out.writeString(new String(bytes, start, len, StandardCharsets.ISO_8859_1));
    }

    @Override
    public void writeFixed(byte[] bytes, int start, int len) throws IOException {
        parser.advance(Symbol.FIXED);
        Symbol.IntCheckAction top = (Symbol.IntCheckAction) parser.popSymbol();
        if (len != top.size) {
            throw new AvroTypeException(
                    "Incorrect length for fixed binary: expected " + top.size + " but received " + len + " bytes.");
        }
        writeByteArray(bytes, start, len);
    }

    @Override
    public void writeEnum(int e) throws IOException {
        parser.advance(Symbol.ENUM);
        Symbol.EnumLabelsAction top = (Symbol.EnumLabelsAction) parser.popSymbol();
        if (e < 0 || e >= top.size) {
            throw new AvroTypeException("Enumeration out of range: max is " + top.size + " but received " + e);
        }
        out.writeString(top.getLabel(e));
    }

    @Override
    public void writeArrayStart() throws IOException {
        parser.advance(Symbol.ARRAY_START);
        out.writeStartArray();
        push();
        isEmpty.set(depth());
    }

    @Override
    public void writeArrayEnd() throws IOException {
        if (!isEmpty.get(pos)) {
            parser.advance(Symbol.ITEM_END);
        }
        pop();
        parser.advance(Symbol.ARRAY_END);
        out.writeEndArray();
    }

    @Override
    public void writeMapStart() throws IOException {
        push();
        isEmpty.set(depth());

        parser.advance(Symbol.MAP_START);
        out.writeStartObject();
    }

    @Override
    public void writeMapEnd() throws IOException {
        if (!isEmpty.get(pos)) {
            parser.advance(Symbol.ITEM_END);
        }
        pop();

        parser.advance(Symbol.MAP_END);
        out.writeEndObject();
    }

    @Override
    public void startItem() throws IOException {
        if (!isEmpty.get(pos)) {
            parser.advance(Symbol.ITEM_END);
        }
        super.startItem();
        isEmpty.clear(depth());
    }

    @Override
    public void writeIndex(int unionIndex) throws IOException {
        parser.advance(Symbol.UNION);
        Symbol.Alternative top = (Symbol.Alternative) parser.popSymbol();
        Symbol symbol = top.getSymbol(unionIndex);
        parser.pushSymbol(symbol);
    }

    @Override
    public Symbol doAction(Symbol input, Symbol top) throws IOException {
        if (top instanceof Symbol.FieldAdjustAction) {
            Symbol.FieldAdjustAction fa = (Symbol.FieldAdjustAction) top;
            out.writeFieldName(fa.fname);
        } else if (top == Symbol.RECORD_START) {
            out.writeStartObject();
        } else if (top == Symbol.RECORD_END || top == Symbol.UNION_END) {
            out.writeEndObject();
        } else if (top != Symbol.FIELD_END) {
            throw new AvroTypeException("Unknown action symbol " + top);
        }
        return null;
    }
}

更新

我创建了一个PR,将此功能添加到JsonEncoder类中。但是,让我们看看他们是否接受它。

https://github.com/apache/avro/pull/508