Author: dcreager
Date: Sat Oct 15 13:30:08 2011
New Revision: 1183634
URL: http://svn.apache.org/viewvc?rev=1183634&view=rev
Log:
AVRO-919. C: Produce JSON encoding for Avro values
This patch adds an avro_value_to_json function, which produces the JSON
encoding on an Avro value. This is a pretty basic translation of the
existing avro_datum_to_json function, which has been modified to use the
new value API. The avro_datum_to_json function is now implemented using
the datum type's value wrapper.
Added:
avro/trunk/lang/c/src/value-json.c
Removed:
avro/trunk/lang/c/src/datum_json.c
Modified:
avro/trunk/CHANGES.txt
avro/trunk/lang/c/src/CMakeLists.txt
avro/trunk/lang/c/src/Makefile.am
avro/trunk/lang/c/src/avro/value.h
avro/trunk/lang/c/src/avrocat.c
avro/trunk/lang/c/src/avropipe.c
Modified: avro/trunk/CHANGES.txt
URL:
http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=1183634&r1=1183633&r2=1183634&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Sat Oct 15 13:30:08 2011
@@ -25,6 +25,9 @@ Avro 1.6.0 (unreleased)
AVRO-893. C: Avro data file functions using new value interface.
(dcreager)
+ AVRO-919. C: Produce JSON encoding of Avro values using new value
+ interface. (dcreager)
+
AVRO-890: Java: Add Maven archetype for creating Avro service
projects. (Stephen Gargan via cutting)
Modified: avro/trunk/lang/c/src/CMakeLists.txt
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/c/src/CMakeLists.txt?rev=1183634&r1=1183633&r2=1183634&view=diff
==============================================================================
--- avro/trunk/lang/c/src/CMakeLists.txt (original)
+++ avro/trunk/lang/c/src/CMakeLists.txt Sat Oct 15 13:30:08 2011
@@ -41,7 +41,6 @@ set(AVRO_SRC
datum.c
datum.h
datum_equal.c
- datum_json.c
datum_read.c
datum_size.c
datum_skip.c
@@ -68,6 +67,7 @@ set(AVRO_SRC
string.c
value.c
value-hash.c
+ value-json.c
value-read.c
value-sizeof.c
value-write.c
Modified: avro/trunk/lang/c/src/Makefile.am
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/c/src/Makefile.am?rev=1183634&r1=1183633&r2=1183634&view=diff
==============================================================================
--- avro/trunk/lang/c/src/Makefile.am (original)
+++ avro/trunk/lang/c/src/Makefile.am Sat Oct 15 13:30:08 2011
@@ -24,10 +24,9 @@ avro/value.h
lib_LTLIBRARIES = libavro.la
libavro_la_SOURCES = st.c st.h schema.c schema.h schema_equal.c \
-value.c value-hash.c value-read.c value-sizeof.c value-write.c \
+value.c value-hash.c value-json.c value-read.c value-sizeof.c value-write.c \
avro_generic_internal.h generic.c \
datum.c datum_equal.c datum_validate.c datum_read.c datum_skip.c datum_write.c
datum_size.c datum.h \
-datum_json.c \
datum_value.c \
array.c map.c memoize.c string.c \
consumer.c consume-binary.c resolver.c \
Modified: avro/trunk/lang/c/src/avro/value.h
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/c/src/avro/value.h?rev=1183634&r1=1183633&r2=1183634&view=diff
==============================================================================
--- avro/trunk/lang/c/src/avro/value.h (original)
+++ avro/trunk/lang/c/src/avro/value.h Sat Oct 15 13:30:08 2011
@@ -354,6 +354,16 @@ avro_value_copy_fast(avro_value_t *dest,
uint32_t
avro_value_hash(avro_value_t *value);
+/*
+ * Returns a string containing the JSON encoding of an Avro value. You
+ * must free this string when you're done with it, using the standard
+ * free() function. (*Not* using the custom Avro allocator.)
+ */
+
+int
+avro_value_to_json(const avro_value_t *value,
+ int one_line, char **json_str);
+
/**
* A helper macro for calling a given method in a value instance, if
Modified: avro/trunk/lang/c/src/avrocat.c
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/c/src/avrocat.c?rev=1183634&r1=1183633&r2=1183634&view=diff
==============================================================================
--- avro/trunk/lang/c/src/avrocat.c (original)
+++ avro/trunk/lang/c/src/avrocat.c Sat Oct 15 13:30:08 2011
@@ -37,16 +37,25 @@ process_file(const char *filename)
exit(1);
}
- avro_datum_t datum;
+ avro_schema_t wschema;
+ avro_value_iface_t *iface;
+ avro_value_t value;
+
+ wschema = avro_file_reader_get_writer_schema(reader);
+ iface = avro_generic_class_from_schema(wschema);
+ avro_generic_value_new(iface, &value);
- while (avro_file_reader_read(reader, NULL, &datum) == 0) {
+ while (avro_file_reader_read_value(reader, &value) == 0) {
char *json;
- avro_datum_to_json(datum, 1, &json);
+ avro_value_to_json(&value, 1, &json);
printf("%s\n", json);
free(json);
+ avro_value_reset(&value);
}
avro_file_reader_close(reader);
+ avro_value_decref(&value);
+ avro_value_iface_decref(iface);
}
Modified: avro/trunk/lang/c/src/avropipe.c
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/c/src/avropipe.c?rev=1183634&r1=1183633&r2=1183634&view=diff
==============================================================================
--- avro/trunk/lang/c/src/avropipe.c (original)
+++ avro/trunk/lang/c/src/avropipe.c Sat Oct 15 13:30:08 2011
@@ -357,6 +357,7 @@ process_file(const char *filename)
for (; avro_file_reader_read_value(reader, &value) == 0;
record_number++) {
create_array_prefix(&prefix, "", record_number);
process_value(avro_raw_string_get(&prefix), &value);
+ avro_value_reset(&value);
}
avro_raw_string_done(&prefix);
Added: avro/trunk/lang/c/src/value-json.c
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/c/src/value-json.c?rev=1183634&view=auto
==============================================================================
--- avro/trunk/lang/c/src/value-json.c (added)
+++ avro/trunk/lang/c/src/value-json.c Sat Oct 15 13:30:08 2011
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "avro/allocation.h"
+#include "avro/errors.h"
+#include "avro/legacy.h"
+#include "avro/schema.h"
+#include "avro/value.h"
+#include "avro_private.h"
+#include "jansson.h"
+
+/*
+ * Converts a binary buffer into a NUL-terminated JSON UTF-8 string.
+ * Avro bytes and fixed values are encoded in JSON as a string, and JSON
+ * strings must be in UTF-8. For these Avro types, the JSON string is
+ * restricted to the characters U+0000..U+00FF, which corresponds to the
+ * ISO-8859-1 character set. This function performs this conversion.
+ * The resulting string must be freed using avro_free when you're done
+ * with it.
+ */
+
+static int
+encode_utf8_bytes(const void *src, size_t src_len,
+ void **dest, size_t *dest_len)
+{
+ check_param(EINVAL, src, "source");
+ check_param(EINVAL, dest, "dest");
+ check_param(EINVAL, dest_len, "dest_len");
+
+ // First, determine the size of the resulting UTF-8 buffer.
+ // Bytes in the range 0x00..0x7f will take up one byte; bytes in
+ // the range 0x80..0xff will take up two.
+ const uint8_t *src8 = src;
+
+ size_t utf8_len = src_len + 1; // +1 for NUL terminator
+ size_t i;
+ for (i = 0; i < src_len; i++) {
+ if (src8[i] & 0x80) {
+ utf8_len++;
+ }
+ }
+
+ // Allocate a new buffer for the UTF-8 string and fill it in.
+ uint8_t *dest8 = avro_malloc(utf8_len);
+ if (dest8 == NULL) {
+ avro_set_error("Cannot allocate JSON bytes buffer");
+ return ENOMEM;
+ }
+
+ uint8_t *curr = dest8;
+ for (i = 0; i < src_len; i++) {
+ if (src8[i] & 0x80) {
+ *curr++ = (0xc0 | (src8[i] >> 6));
+ *curr++ = (0x80 | (src8[i] & 0x3f));
+ } else {
+ *curr++ = src8[i];
+ }
+ }
+
+ *curr = '\0';
+
+ // And we're good.
+ *dest = dest8;
+ *dest_len = utf8_len;
+ return 0;
+}
+
+#define return_json(type, exp) \
+ { \
+ json_t *result = exp; \
+ if (result == NULL) { \
+ avro_set_error("Cannot allocate JSON " type); \
+ } \
+ return result; \
+ }
+
+#define check_return(retval, call) \
+ do { \
+ int __rc; \
+ __rc = call; \
+ if (__rc != 0) { \
+ return retval; \
+ } \
+ } while (0)
+
+static json_t *
+avro_value_to_json_t(const avro_value_t *value)
+{
+ switch (avro_value_get_type(value)) {
+ case AVRO_BOOLEAN:
+ {
+ int val;
+ check_return(NULL, avro_value_get_boolean(value, &val));
+ return_json("boolean",
+ val? json_true(): json_false());
+ }
+
+ case AVRO_BYTES:
+ {
+ const void *val;
+ size_t size;
+ void *encoded = NULL;
+ size_t encoded_size = 0;
+
+ check_return(NULL, avro_value_get_bytes(value, &val,
&size));
+
+ if (encode_utf8_bytes(val, size, &encoded,
&encoded_size)) {
+ return NULL;
+ }
+
+ json_t *result = json_string_nocheck(encoded);
+ avro_free(encoded, encoded_size);
+ if (result == NULL) {
+ avro_set_error("Cannot allocate JSON bytes");
+ }
+ return result;
+ }
+
+ case AVRO_DOUBLE:
+ {
+ double val;
+ check_return(NULL, avro_value_get_double(value, &val));
+ return_json("double", json_real(val));
+ }
+
+ case AVRO_FLOAT:
+ {
+ float val;
+ check_return(NULL, avro_value_get_float(value, &val));
+ return_json("float", json_real(val));
+ }
+
+ case AVRO_INT32:
+ {
+ int32_t val;
+ check_return(NULL, avro_value_get_int(value, &val));
+ return_json("int", json_integer(val));
+ }
+
+ case AVRO_INT64:
+ {
+ int64_t val;
+ check_return(NULL, avro_value_get_long(value, &val));
+ return_json("long", json_integer(val));
+ }
+
+ case AVRO_NULL:
+ {
+ check_return(NULL, avro_value_get_null(value));
+ return_json("null", json_null());
+ }
+
+ case AVRO_STRING:
+ {
+ const char *val;
+ size_t size;
+ check_return(NULL, avro_value_get_string(value, &val,
&size));
+ return_json("string", json_string(val));
+ }
+
+ case AVRO_ARRAY:
+ {
+ int rc;
+ size_t element_count, i;
+ json_t *result = json_array();
+ if (result == NULL) {
+ avro_set_error("Cannot allocate JSON array");
+ return NULL;
+ }
+
+ rc = avro_value_get_size(value, &element_count);
+ if (rc != 0) {
+ json_decref(result);
+ return NULL;
+ }
+
+ for (i = 0; i < element_count; i++) {
+ avro_value_t element;
+ rc = avro_value_get_by_index(value, i,
&element, NULL);
+ if (rc != 0) {
+ json_decref(result);
+ return NULL;
+ }
+
+ json_t *element_json =
avro_value_to_json_t(&element);
+ if (element_json == NULL) {
+ json_decref(result);
+ return NULL;
+ }
+
+ if (json_array_append_new(result,
element_json)) {
+ avro_set_error("Cannot append element
to array");
+ json_decref(result);
+ return NULL;
+ }
+ }
+
+ return result;
+ }
+
+ case AVRO_ENUM:
+ {
+ avro_schema_t enum_schema;
+ int symbol_value;
+ const char *symbol_name;
+
+ check_return(NULL, avro_value_get_enum(value,
&symbol_value));
+ enum_schema = avro_value_get_schema(value);
+ symbol_name = avro_schema_enum_get(enum_schema,
symbol_value);
+ return_json("enum", json_string(symbol_name));
+ }
+
+ case AVRO_FIXED:
+ {
+ const void *val;
+ size_t size;
+ void *encoded = NULL;
+ size_t encoded_size = 0;
+
+ check_return(NULL, avro_value_get_fixed(value, &val,
&size));
+
+ if (encode_utf8_bytes(val, size, &encoded,
&encoded_size)) {
+ return NULL;
+ }
+
+ json_t *result = json_string_nocheck(encoded);
+ avro_free(encoded, encoded_size);
+ if (result == NULL) {
+ avro_set_error("Cannot allocate JSON fixed");
+ }
+ return result;
+ }
+
+ case AVRO_MAP:
+ {
+ int rc;
+ size_t element_count, i;
+ json_t *result = json_object();
+ if (result == NULL) {
+ avro_set_error("Cannot allocate JSON map");
+ return NULL;
+ }
+
+ rc = avro_value_get_size(value, &element_count);
+ if (rc != 0) {
+ json_decref(result);
+ return NULL;
+ }
+
+ for (i = 0; i < element_count; i++) {
+ const char *key;
+ avro_value_t element;
+
+ rc = avro_value_get_by_index(value, i,
&element, &key);
+ if (rc != 0) {
+ json_decref(result);
+ return NULL;
+ }
+
+ json_t *element_json =
avro_value_to_json_t(&element);
+ if (element_json == NULL) {
+ json_decref(result);
+ return NULL;
+ }
+
+ if (json_object_set_new(result, key,
element_json)) {
+ avro_set_error("Cannot append element
to map");
+ json_decref(result);
+ return NULL;
+ }
+ }
+
+ return result;
+ }
+
+ case AVRO_RECORD:
+ {
+ int rc;
+ size_t field_count, i;
+ json_t *result = json_object();
+ if (result == NULL) {
+ avro_set_error("Cannot allocate new JSON
record");
+ return NULL;
+ }
+
+ rc = avro_value_get_size(value, &field_count);
+ if (rc != 0) {
+ json_decref(result);
+ return NULL;
+ }
+
+ for (i = 0; i < field_count; i++) {
+ const char *field_name;
+ avro_value_t field;
+
+ rc = avro_value_get_by_index(value, i, &field,
&field_name);
+ if (rc != 0) {
+ json_decref(result);
+ return NULL;
+ }
+
+ json_t *field_json =
avro_value_to_json_t(&field);
+ if (field_json == NULL) {
+ json_decref(result);
+ return NULL;
+ }
+
+ if (json_object_set_new(result, field_name,
field_json)) {
+ avro_set_error("Cannot append field to
record");
+ json_decref(result);
+ return NULL;
+ }
+ }
+
+ return result;
+ }
+
+ case AVRO_UNION:
+ {
+ int disc;
+ avro_value_t branch;
+ avro_schema_t union_schema;
+ avro_schema_t branch_schema;
+ const char *branch_name;
+
+ check_return(NULL, avro_value_get_current_branch(value,
&branch));
+
+ if (avro_value_get_type(&branch) == AVRO_NULL) {
+ return_json("null", json_null());
+ }
+
+ check_return(NULL, avro_value_get_discriminant(value,
&disc));
+ union_schema = avro_value_get_schema(value);
+ branch_schema =
+ avro_schema_union_branch(union_schema, disc);
+ branch_name = avro_schema_type_name(branch_schema);
+
+ json_t *result = json_object();
+ if (result == NULL) {
+ avro_set_error("Cannot allocate JSON union");
+ return NULL;
+ }
+
+ json_t *branch_json = avro_value_to_json_t(&branch);
+ if (branch_json == NULL) {
+ json_decref(result);
+ return NULL;
+ }
+
+ if (json_object_set_new(result, branch_name,
branch_json)) {
+ avro_set_error("Cannot append branch to union");
+ json_decref(result);
+ return NULL;
+ }
+
+ return result;
+ }
+
+ default:
+ return NULL;
+ }
+}
+
+int
+avro_value_to_json(const avro_value_t *value,
+ int one_line, char **json_str)
+{
+ check_param(EINVAL, value, "value");
+ check_param(EINVAL, json_str, "string buffer");
+
+ json_t *json = avro_value_to_json_t(value);
+ if (json == NULL) {
+ return ENOMEM;
+ }
+
+ /*
+ * Jansson will only encode an object or array as the root
+ * element.
+ */
+
+ *json_str = json_dumps
+ (json,
+ JSON_ENCODE_ANY |
+ JSON_INDENT(one_line? 0: 2) |
+ JSON_ENSURE_ASCII |
+ JSON_PRESERVE_ORDER);
+ json_decref(json);
+ return 0;
+}
+
+int
+avro_datum_to_json(const avro_datum_t datum,
+ int one_line, char **json_str)
+{
+ avro_value_t value;
+ avro_datum_as_value(&value, datum);
+ return avro_value_to_json(&value, one_line, json_str);
+}