public interface JsonAvroBinding extends AvroBinding<JsonRecord>
JsonAvroBinding
interface has the same methods as AvroBinding
, but represents values as instances of JsonRecord
.
A single schema binding is created using AvroCatalog.getJsonBinding(org.apache.avro.Schema)
, and a multiple schema binding is created
using AvroCatalog.getJsonMultiBinding(java.util.Map<java.lang.String, org.apache.avro.Schema>)
.
The trade-offs in using a JsonAvroBinding
, compared to other types
of bindings, are:
GenericRecord
, certain Avro data types are not
represented conveniently using JSON syntax, namely:
int
and long
are both represented as a JSON
integer
, and Avro float
and double
are both
represented as a JSON number
. WARNING: To avoid type conversion
errors, consider defining integer fields in the Avro schema using type
long
, and floating point fields using type double
.bytes
and fixed
are both represented as a
JSON string
, using Unicode escape syntax. WARNING: Characters
greater than 0xFF
are invalid because they cannot be translated
to bytes without loss of information. Also note that the Jackson
BinaryNode
class is not
used with these Avro types.Also note the following type mappings:
record
and map
are both represented as a JSON
object
.enum
is represented as a JSON string
.GenericRecord
, a JSON object does not provide type
safety. It can also be error prone, because fields are accessed by
string name.
Schema
objects at runtime. In other words, the application
must maintain a set of known schemas for use at runtime.
See AvroCatalog
for general information on Avro bindings and
schemas. The schemas used in the examples below are described in the AvroCatalog
javadoc.
When using a JsonAvroBinding
, a JsonRecord
is used to
represent values. A JsonRecord
represents an Avro object as an
JsonNode
in the Jackson API.
As with a generic binding, a JsonNode
represents an Avro object
roughly as a map of string field names to field values. However, the
Jackson API is very rich and fully supports the JSON format. For those
familiar with XML, the Jackson API is for JSON what the DOM API is for XML.
JSON text may also be easily parsed and formatted using the Jackson API.
The following code fragment demonstrates writing and reading a value using a JSON single schema binding.
Schema.Parser parser = new Schema.Parser(); Schema nameSchema = parser.parse(nameSchemaText); Schema memberSchema = parser.parse(memberSchemaText); JsonAvroBinding binding = avroCatalog.getJsonBinding(memberSchema); // Create object ObjectNode member = JsonNodeFactory.instance.objectNode(); JsonRecord object = new JsonRecord(member, memberSchema) ObjectNode name = member.putObject("name"); name.put("first", ...); name.put("last", ...); member.put("age", ...); // Serialize and store Value value = binding.toValue(object); kvStore.put(key, value); // Sometime later, retrieve and deserialize ValueVersion vv = kvStore.get(key); JsonRecord object = binding.toObject(vv.getValue()); // Use object ObjectNode member = (ObjectNode) object.getNode(); ObjectNode name = (ObjectNode) member.get("name"); int age = member.get("age").getIntValue(); ...
The following code fragment demonstrates reading values with different schemas using a JSON multiple schema binding.
Schema.Parser parser = new Schema.Parser(); Schema nameSchema = parser.parse(nameSchemaText); Schema memberSchema = parser.parse(memberSchemaText); Schema anotherSchema = parser.parse(anotherSchemaText); Map<String, Schema> schemas = new HashMap<String, Schema>() schemas.put(memberSchema.getFullName(), memberSchema); schemas.put(anotherSchema.getFullName(), anotherSchema); JsonAvroBinding binding = avroCatalog.getJsonMultiBinding(schemas); Iterator<KeyValueVersion> iter = kvStore.multiGetIterator(...); for (KeyValueVersion kvv : iter) { JsonRecord object = binding.toObject(kvv.getValue()); JsonNode jsonNode = object.getJsonNode(); String schemaName = object.getSchema().getFullName(); if (schemaName.equals(memberSchema.getFullName())) { ... } else if (schemaName.equals(anotherSchema.getFullName())) { ... } else { ... } }
A special use case for a JSON multiple schema binding is when the
application treats values dynamically based on their schema, rather than
using a fixed set of known schemas. The AvroCatalog.getCurrentSchemas()
method can be used to obtain a map of the
most current schemas, which can be passed to AvroCatalog.getJsonMultiBinding(java.util.Map<java.lang.String, org.apache.avro.Schema>)
.
For example, the following code fragment demonstrates reading values with
different schemas using a JSON multiple schema binding. Note that in a long
running application, it is possible that a schema may be added and used to
store a key-value pair, after the binding has been created. The application
may handle this possibility by catching SchemaNotAllowedException
.
JsonAvroBinding binding = avroCatalog.getJsonMultiBinding(avroCatalog.getCurrentSchemas()); Iterator<KeyValueVersion> iter = kvStore.storeIterator(...); for (KeyValueVersion kvv : iter) { JsonRecord object; try { object = binding.toObject(kvv.getValue()); } catch (SchemaNotAllowedException e) { // In this example, ignore values with a schema that was not // known at the time the binding was created. continue; } JsonNode jsonNode = object.getJsonNode(); String schemaName = object.getSchema().getFullName(); if (schemaName.equals(memberSchema.getFullName())) { ... } else if (schemaName.equals(anotherSchema.getFullName())) { ... } else { ... } }
Modifier and Type | Method and Description |
---|---|
JsonRecord |
toObject(Value value)
|
Value |
toValue(JsonRecord object)
Before doing a write operation, the user calls
toValue passing
an object she wishes to store. |
JsonRecord toObject(Value value) throws SchemaNotAllowedException, IllegalArgumentException
KVStore
method, the user
calls toObject
with the Value
obtained from the read
operation.
If necessary, this method automatically performs schema evolution, as
described in AvroCatalog
. In the context of schema evolution,
the writer schema is the one associated internally with the value
parameter (this association was normally made earlier when the
value was stored), and the reader schema is the one associated with this
binding (and was specified when the binding was created).
In other words, this method transforms the serialized data in the value
parameter to conform to the schema of the JsonRecord
that
is returned.
toObject
in interface AvroBinding<JsonRecord>
toObject
in interface ValueBinding<JsonRecord>
value
- the Value
obtained from a KVStore
read
operation method.. The byte array of the Value
is
serialized Avro data, packaged in an internal format that includes a
reference to the Avro schemaJsonRecord
instance. The JsonRecord.getSchema()
method will return the reader schema, which is the
schema that was specified when this binding was created.SchemaNotAllowedException
- if the schema associated with the
value
parameter is not allowed with this binding.IllegalArgumentException
- if the value format is not Value.Format.AVRO
, the schema identifier embedded in the value
parameter is invalid, or the serialized data cannot be parsed.Value toValue(JsonRecord object) throws SchemaNotAllowedException, UndefinedSchemaException, IllegalArgumentException
toValue
passing
an object she wishes to store. The resulting Value
is then
passed to the write operation method in KVStore
.
In the context of schema evolution, as described in AvroCatalog
,
the returned value is serialized according to the writer schema. The
writer schema is the one associated with the JsonRecord
object
parameter; it is returned by JsonRecord.getSchema()
and specified when creating a JsonRecord
object. The writer
schema must be one of the schemas specified when this binding was
created.
In other words, this method returns serialized data that conforms to the
schema of the given JsonRecord
.
toValue
in interface AvroBinding<JsonRecord>
toValue
in interface ValueBinding<JsonRecord>
object
- the JsonRecord
instance the user wishes to
store, or at least serialize.Value
is
serialized Avro data, packaged in an internal format that includes a
reference to the Avro schemaSchemaNotAllowedException
- if the schema associated with the
object
parameter is not allowed with this binding.UndefinedSchemaException
- if the schema associated with the
object
parameter has not been defined using the NoSQL Database
administration interface. Note that when the allowed schemas for a
binding are specified (and validate) at the time the binding is created,
this exception is extremely unlikely and is only possible if a schema is
mistakenly disabled after the binding is created.IllegalArgumentException
- if the object
parameter is
invalid according to its schema, and cannot be serialized.Copyright (c) 2011, 2014 Oracle and/or its affiliates. All rights reserved.