kv-4.0.9.src.oracle.kv.avro.JsonAvroBinding Maven / Gradle / Ivy
Show all versions of oracle-nosql-client Show documentation
/*-
*
* This file is part of Oracle NoSQL Database
* Copyright (C) 2011, 2016 Oracle and/or its affiliates. All rights reserved.
*
* If you have received this file as part of Oracle NoSQL Database the
* following applies to the work as a whole:
*
* Oracle NoSQL Database server software is free software: you can
* redistribute it and/or modify it under the terms of the GNU Affero
* General Public License as published by the Free Software Foundation,
* version 3.
*
* Oracle NoSQL Database is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* If you have received this file as part of Oracle NoSQL Database Client or
* distributed separately the following applies:
*
* Oracle NoSQL Database client software is free software: you can
* redistribute it and/or modify it under the terms of the Apache License
* as published by the Apache Software Foundation, version 2.0.
*
* You should have received a copy of the GNU Affero General Public License
* and/or the Apache License in the LICENSE file along with Oracle NoSQL
* Database client or server distribution. If not, see
*
* or
* .
*
* An active Oracle commercial licensing agreement for this product supersedes
* these licenses and in such case the license notices, but not the copyright
* notice, may be removed by you in connection with your distribution that is
* in accordance with the commercial licensing terms.
*
* For more information please contact:
*
* [email protected]
*
*/
package oracle.kv.avro;
import oracle.kv.Value;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericRecord;
import org.codehaus.jackson.JsonNode;
/**
* The {@code JsonAvroBinding} interface has the same methods as {@link
* AvroBinding}, but represents values as instances of {@link JsonRecord}.
* A single schema binding is created using {@link
* AvroCatalog#getJsonBinding}, and a multiple schema binding is created
* using {@link AvroCatalog#getJsonMultiBinding}.
*
* The trade-offs in using a {@code JsonAvroBinding}, compared to other types
* of bindings, are:
*
* - Probably the most important reason for using a JSON binding is for
* interoperability with other components or external systems that use JSON
* objects.
*
* - Like generic bindings, an advantage of using a JSON binding is that
* values may be treated generically. Also, if schemas are treated
* dynamically, then the set of schemas used in the application need not be
* fixed at build time.
*
* - Unlike {@link GenericRecord}, certain Avro data types are not
* represented conveniently using JSON syntax, namely:
*
* - Avro {@code int} and {@code long} are both represented as a JSON
* {@code integer}, and Avro {@code float} and {@code double} are both
* represented as a JSON {@code number}. WARNING: To avoid type conversion
* errors, consider defining integer fields in the Avro schema using type
* {@code long}, and floating point fields using type {@code double}.
* - Avro {@code bytes} and {@code fixed} are both represented as a
* JSON {@code string}, using Unicode escape syntax. WARNING: Characters
* greater than {@code 0xFF} are invalid because they cannot be translated
* to bytes without loss of information. Also note that the Jackson
* {@link org.codehaus.jackson.node.BinaryNode} class is not
* used with these Avro types.
* - Avro unions have a special
* JSON representation.
*
* Therefore, applications using JSON should limit the data types used in
* their Avro schemas, and should treat the above data types carefully.
*
* Also note the following type mappings:
*
* - Avro {@code record} and {@code map} are both represented as a JSON
* {@code object}.
* - An Avro {@code enum} is represented as a JSON {@code string}.
*
*
* - Like a {@link GenericRecord}, a JSON object does not provide type
* safety. It can also be error prone, because fields are accessed by
* string name.
*
* - To support class evolution, with JSON bindings (like generic
* bindings, but unlike specific bindings) the application must supply the
* Avro {@link Schema} objects at runtime. In other words, the application
* must maintain a set of known schemas for use at runtime.
*
*
*
* See {@link AvroCatalog} for general information on Avro bindings and
* schemas. The schemas used in the examples below are described in the {@link
* AvroCatalog} javadoc.
*
* When using a {@code JsonAvroBinding}, a {@link JsonRecord} is used to
* represent values. A {@link JsonRecord} represents an Avro object as an
* {@link JsonNode} in the Jackson API.
*
* As with a generic binding, a {@link 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 {@link
* AvroCatalog#getCurrentSchemas} method can be used to obtain a map of the
* most current schemas, which can be passed to {@link
* AvroCatalog#getJsonMultiBinding}.
*
* 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 {@link 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 {
* ...
* }
* }
*
* @since 2.0
*
* @deprecated as of 4.0, use the table API instead.
*/
@Deprecated
public interface JsonAvroBinding extends AvroBinding {
/**
* {@inheritDoc}
*
* If necessary, this method automatically performs schema evolution, as
* described in {@link AvroCatalog}. In the context of schema evolution,
* the writer schema is the one associated internally with the {@code
* 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 {@code
* value} parameter to conform to the schema of the {@link JsonRecord} that
* is returned.
*
* @return the deserialized {@link JsonRecord} instance. The {@link
* JsonRecord#getSchema} method will return the reader schema, which is the
* schema that was specified when this binding was created.
*/
@Override
public JsonRecord toObject(Value value)
throws SchemaNotAllowedException, IllegalArgumentException;
/**
* {@inheritDoc}
*
* In the context of schema evolution, as described in {@link AvroCatalog},
* the returned value is serialized according to the writer schema. The
* writer schema is the one associated with the {@link JsonRecord}
* {@code object} parameter; it is returned by {@link JsonRecord#getSchema}
* and specified when creating a {@link 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 {@link JsonRecord}.
*
* @param object the {@link JsonRecord} instance the user wishes to
* store, or at least serialize.
*/
@Override
public Value toValue(JsonRecord object)
throws SchemaNotAllowedException, UndefinedSchemaException,
IllegalArgumentException;
/**
* Deserialization to JsonRecord using RawBinding, BinaryDecoder and
* JsonDatumReader used by import utility. The schema needed for
* deserialization is provided by the export package.
*/
JsonRecord toObjectForImport(Value value, Schema schema)
throws SchemaNotAllowedException, IllegalArgumentException;
}