diff mbox series

[ovs-dev,3/4] json: Use functions to access json arrays.

Message ID 20250606112253.3314013-4-i.maximets@ovn.org
State Changes Requested
Headers show
Series json: Store short strings and arrays in-place. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

Ilya Maximets June 6, 2025, 11:22 a.m. UTC
Internal implementation of JSON array will be changed in the future
commits.  Add access functions that users can rely on instead of
accessing the internals of 'struct json' directly and convert all the
users.  Structure fields are intentionally renamed to make sure that
no code is using the old fields directly.

json_array() function is removed, as not needed anymore.  Added new
functions:  json_array_size(), json_array_at(), json_array_set()
and json_array_pop().  These are enough to cover all the use cases
within OVS.

The change is fairly large, however, IMO, it's a much overdue cleanup
that we need even without changing the underlying implementation.

Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
---
 include/openvswitch/json.h |   9 +-
 lib/json.c                 | 179 ++++++++++++++++++++++---------------
 lib/ovsdb-cs.c             |  49 +++++-----
 lib/ovsdb-data.c           |  26 +++---
 lib/ovsdb-idl.c            |  57 ++++++------
 lib/unixctl.c              |  18 ++--
 ovsdb/column.c             |  11 ++-
 ovsdb/condition.c          |  24 +++--
 ovsdb/execution.c          |  20 +++--
 ovsdb/jsonrpc-server.c     | 104 ++++++++++-----------
 ovsdb/mutation.c           |  33 ++++---
 ovsdb/ovsdb-client.c       | 127 +++++++++++++-------------
 ovsdb/ovsdb-server.c       |  10 ++-
 ovsdb/ovsdb-tool.c         |  30 +++----
 ovsdb/raft-private.c       |  11 +--
 ovsdb/raft-private.h       |   3 +-
 ovsdb/raft-rpc.c           |  16 ++--
 ovsdb/replication.c        |  23 ++---
 ovsdb/storage.c            |  17 ++--
 ovsdb/table.c              |   8 +-
 ovsdb/trigger.c            |   4 +-
 python/ovs/_json.c         |   8 +-
 tests/test-json.c          |  12 +--
 tests/test-ovsdb.c         |  95 ++++++++++----------
 24 files changed, 483 insertions(+), 411 deletions(-)
diff mbox series

Patch

diff --git a/include/openvswitch/json.h b/include/openvswitch/json.h
index 6e747f5d9..e556ceadd 100644
--- a/include/openvswitch/json.h
+++ b/include/openvswitch/json.h
@@ -59,8 +59,8 @@  const char *json_type_to_string(enum json_type);
 
 /* A JSON array. */
 struct json_array {
-    size_t n, n_allocated;
-    struct json **elems;
+    size_t size, allocated;
+    struct json **elements;
 };
 
 /* Maximum string length that can be stored inline ('\0' is not included). */
@@ -99,6 +99,8 @@  struct json *json_real_create(double);
 
 struct json *json_array_create_empty(void);
 void json_array_add(struct json *, struct json *element);
+void json_array_set(struct json *, size_t index, struct json *element);
+struct json *json_array_pop(struct json *);
 void json_array_trim(struct json *);
 struct json *json_array_create(struct json **, size_t n);
 struct json *json_array_create_1(struct json *);
@@ -119,7 +121,8 @@  void json_object_put_uuid(struct json *, const char *name,
 
 const char *json_string(const struct json *);
 const char *json_serialized_object(const struct json *);
-struct json_array *json_array(const struct json *);
+size_t json_array_size(const struct json *);
+const struct json *json_array_at(const struct json *, size_t index);
 struct shash *json_object(const struct json *);
 bool json_boolean(const struct json *);
 double json_real(const struct json *);
diff --git a/lib/json.c b/lib/json.c
index 061fb24de..1214268b1 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -226,30 +226,44 @@  struct json *
 json_array_create_empty(void)
 {
     struct json *json = json_create(JSON_ARRAY);
-    json->array.elems = NULL;
-    json->array.n = 0;
-    json->array.n_allocated = 0;
+    json->array.elements = NULL;
+    json->array.size = 0;
+    json->array.allocated = 0;
     return json;
 }
 
 void
 json_array_add(struct json *array_, struct json *element)
 {
-    struct json_array *array = json_array(array_);
-    if (array->n >= array->n_allocated) {
-        array->elems = x2nrealloc(array->elems, &array->n_allocated,
-                                  sizeof *array->elems);
+    struct json_array *array = &array_->array;
+    if (array->size >= array->allocated) {
+        array->elements = x2nrealloc(array->elements, &array->allocated,
+                                     sizeof *array->elements);
     }
-    array->elems[array->n++] = element;
+    array->elements[array->size++] = element;
+}
+
+void
+json_array_set(struct json *json, size_t index, struct json *element)
+{
+    ovs_assert(json_array_size(json) > index);
+    json->array.elements[index] = element;
+}
+
+struct json *
+json_array_pop(struct json *json)
+{
+    return json->array.size ? json->array.elements[--json->array.size] : NULL;
 }
 
 void
 json_array_trim(struct json *array_)
 {
-    struct json_array *array = json_array(array_);
-    if (array->n < array->n_allocated){
-        array->n_allocated = array->n;
-        array->elems = xrealloc(array->elems, array->n * sizeof *array->elems);
+    struct json_array *array = &array_->array;
+    if (array->size < array->allocated) {
+        array->allocated = array->size;
+        array->elements = xrealloc(array->elements,
+                                   array->size * sizeof *array->elements);
     }
 }
 
@@ -257,46 +271,46 @@  struct json *
 json_array_create(struct json **elements, size_t n)
 {
     struct json *json = json_create(JSON_ARRAY);
-    json->array.elems = elements;
-    json->array.n = n;
-    json->array.n_allocated = n;
+    json->array.elements = elements;
+    json->array.size = n;
+    json->array.allocated = n;
     return json;
 }
 
 struct json *
 json_array_create_1(struct json *elem0)
 {
-    struct json **elems = xmalloc(sizeof *elems);
-    elems[0] = elem0;
-    return json_array_create(elems, 1);
+    struct json **elements = xmalloc(sizeof *elements);
+    elements[0] = elem0;
+    return json_array_create(elements, 1);
 }
 
 struct json *
 json_array_create_2(struct json *elem0, struct json *elem1)
 {
-    struct json **elems = xmalloc(2 * sizeof *elems);
-    elems[0] = elem0;
-    elems[1] = elem1;
-    return json_array_create(elems, 2);
+    struct json **elements = xmalloc(2 * sizeof *elements);
+    elements[0] = elem0;
+    elements[1] = elem1;
+    return json_array_create(elements, 2);
 }
 
 struct json *
 json_array_create_3(struct json *elem0, struct json *elem1, struct json *elem2)
 {
-    struct json **elems = xmalloc(3 * sizeof *elems);
-    elems[0] = elem0;
-    elems[1] = elem1;
-    elems[2] = elem2;
-    return json_array_create(elems, 3);
+    struct json **elements = xmalloc(3 * sizeof *elements);
+    elements[0] = elem0;
+    elements[1] = elem1;
+    elements[2] = elem2;
+    return json_array_create(elements, 3);
 }
 
 bool
 json_array_contains_string(const struct json *json, const char *str)
 {
-    ovs_assert(json->type == JSON_ARRAY);
+    size_t n = json_array_size(json);
 
-    for (size_t i = 0; i < json->array.n; i++) {
-        const struct json *elem = json->array.elems[i];
+    for (size_t i = 0; i < n; i++) {
+        const struct json *elem = json_array_at(json, i);
 
         if (elem->type == JSON_STRING && !strcmp(json_string(elem), str)) {
             return true;
@@ -381,11 +395,18 @@  json_serialized_object(const struct json *json)
     return json->str_ptr;
 }
 
-struct json_array *
-json_array(const struct json *json)
+size_t
+json_array_size(const struct json *json)
+{
+    ovs_assert(json->type == JSON_ARRAY);
+    return json->array.size;
+}
+
+const struct json *
+json_array_at(const struct json *json, size_t index)
 {
     ovs_assert(json->type == JSON_ARRAY);
-    return CONST_CAST(struct json_array *, &json->array);
+    return (json->array.size > index) ? json->array.elements[index] : NULL;
 }
 
 struct shash *
@@ -417,7 +438,7 @@  json_integer(const struct json *json)
 }
 
 static void json_destroy_object(struct shash *object, bool yield);
-static void json_destroy_array(struct json_array *array, bool yield);
+static void json_destroy_array(struct json *json, bool yield);
 
 /* Frees 'json' and everything it points to, recursively. */
 void
@@ -429,7 +450,7 @@  json_destroy__(struct json *json, bool yield)
         break;
 
     case JSON_ARRAY:
-        json_destroy_array(&json->array, yield);
+        json_destroy_array(json, yield);
         break;
 
     case JSON_STRING:
@@ -479,26 +500,27 @@  json_destroy_object(struct shash *object, bool yield)
 }
 
 static void
-json_destroy_array(struct json_array *array, bool yield)
+json_destroy_array(struct json *json, bool yield)
 {
-    size_t i;
+    size_t i, n = json_array_size(json);
 
     if (yield) {
         cooperative_multitasking_yield();
     }
 
-    for (i = 0; i < array->n; i++) {
+    for (i = 0; i < n; i++) {
         if (yield) {
-            json_destroy_with_yield(array->elems[i]);
+            json_destroy_with_yield(
+                CONST_CAST(struct json *, json_array_at(json, i)));
         } else {
-            json_destroy(array->elems[i]);
+            json_destroy(CONST_CAST(struct json *, json_array_at(json, i)));
         }
     }
-    free(array->elems);
+    free(json->array.elements);
 }
 
 static struct json *json_deep_clone_object(const struct shash *object);
-static struct json *json_deep_clone_array(const struct json_array *array);
+static struct json *json_deep_clone_array(const struct json *);
 
 /* Returns a deep copy of 'json'. */
 struct json *
@@ -509,7 +531,7 @@  json_deep_clone(const struct json *json)
         return json_deep_clone_object(json->object);
 
     case JSON_ARRAY:
-        return json_deep_clone_array(&json->array);
+        return json_deep_clone_array(json);
 
     case JSON_STRING:
         return json_string_create(json_string(json));
@@ -555,16 +577,33 @@  json_deep_clone_object(const struct shash *object)
 }
 
 static struct json *
-json_deep_clone_array(const struct json_array *array)
-{
-    struct json **elems;
-    size_t i;
+json_deep_clone_array(const struct json *json)
+{
+    struct json **elements;
+    size_t i, n;
+
+    n = json_array_size(json);
+    switch (n) {
+    case 0:
+        return json_array_create_empty();
+    case 1:
+        return json_array_create_1(json_deep_clone(json_array_at(json, 0)));
+    case 2:
+        return json_array_create_2(json_deep_clone(json_array_at(json, 0)),
+                                   json_deep_clone(json_array_at(json, 1)));
+    case 3:
+        return json_array_create_3(json_deep_clone(json_array_at(json, 0)),
+                                   json_deep_clone(json_array_at(json, 1)),
+                                   json_deep_clone(json_array_at(json, 2)));
+    default:
+        break;
+    }
 
-    elems = xmalloc(array->n * sizeof *elems);
-    for (i = 0; i < array->n; i++) {
-        elems[i] = json_deep_clone(array->elems[i]);
+    elements = xmalloc(n * sizeof *elements);
+    for (i = 0; i < n; i++) {
+        elements[i] = json_deep_clone(json_array_at(json, i));
     }
-    return json_array_create(elems, array->n);
+    return json_array_create(elements, n);
 }
 
 static size_t
@@ -585,13 +624,13 @@  json_hash_object(const struct shash *object, size_t basis)
 }
 
 static size_t
-json_hash_array(const struct json_array *array, size_t basis)
+json_hash_array(const struct json *json, size_t basis)
 {
-    size_t i;
+    size_t i, n = json_array_size(json);
 
-    basis = hash_int(array->n, basis);
-    for (i = 0; i < array->n; i++) {
-        basis = json_hash(array->elems[i], basis);
+    basis = hash_int(n, basis);
+    for (i = 0; i < n; i++) {
+        basis = json_hash(json_array_at(json, i), basis);
     }
     return basis;
 }
@@ -604,7 +643,7 @@  json_hash(const struct json *json, size_t basis)
         return json_hash_object(json->object, basis);
 
     case JSON_ARRAY:
-        return json_hash_array(&json->array, basis);
+        return json_hash_array(json, basis);
 
     case JSON_STRING:
         return hash_string(json_string(json), basis);
@@ -649,16 +688,16 @@  json_equal_object(const struct shash *a, const struct shash *b)
 }
 
 static bool
-json_equal_array(const struct json_array *a, const struct json_array *b)
+json_equal_array(const struct json *a, const struct json *b)
 {
-    size_t i;
+    size_t i, n = json_array_size(a);
 
-    if (a->n != b->n) {
+    if (n != json_array_size(b)) {
         return false;
     }
 
-    for (i = 0; i < a->n; i++) {
-        if (!json_equal(a->elems[i], b->elems[i])) {
+    for (i = 0; i < n; i++) {
+        if (!json_equal(json_array_at(a, i), json_array_at(b, i))) {
             return false;
         }
     }
@@ -682,7 +721,7 @@  json_equal(const struct json *a, const struct json *b)
         return json_equal_object(a->object, b->object);
 
     case JSON_ARRAY:
-        return json_equal_array(&a->array, &b->array);
+        return json_equal_array(a, b);
 
     case JSON_STRING:
         return !strcmp(json_string(a), json_string(b));
@@ -1605,7 +1644,7 @@  struct json_serializer {
 static void json_serialize(const struct json *, struct json_serializer *);
 static void json_serialize_object(const struct shash *object,
                                   struct json_serializer *);
-static void json_serialize_array(const struct json_array *,
+static void json_serialize_array(const struct json *,
                                  struct json_serializer *);
 static void json_serialize_string(const char *, struct ds *);
 
@@ -1668,7 +1707,7 @@  json_serialize(const struct json *json, struct json_serializer *s)
         break;
 
     case JSON_ARRAY:
-        json_serialize_array(&json->array, s);
+        json_serialize_array(json, s);
         break;
 
     case JSON_INTEGER:
@@ -1760,10 +1799,10 @@  json_serialize_object(const struct shash *object, struct json_serializer *s)
 }
 
 static void
-json_serialize_array(const struct json_array *array, struct json_serializer *s)
+json_serialize_array(const struct json *json, struct json_serializer *s)
 {
+    size_t i, n = json_array_size(json);
     struct ds *ds = s->ds;
-    size_t i;
 
     ds_put_char(ds, '[');
     s->depth++;
@@ -1772,15 +1811,15 @@  json_serialize_array(const struct json_array *array, struct json_serializer *s)
         cooperative_multitasking_yield();
     }
 
-    if (array->n > 0) {
+    if (n > 0) {
         indent_line(s);
 
-        for (i = 0; i < array->n; i++) {
+        for (i = 0; i < n; i++) {
             if (i) {
                 ds_put_char(ds, ',');
                 indent_line(s);
             }
-            json_serialize(array->elems[i], s);
+            json_serialize(json_array_at(json, i), s);
         }
     }
 
diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
index 20386101f..eaf0867de 100644
--- a/lib/ovsdb-cs.c
+++ b/lib/ovsdb-cs.c
@@ -843,15 +843,17 @@  static enum condition_type
 condition_classify(const struct json *condition)
 {
     if (condition) {
-        const struct json_array *a = json_array(condition);
-        switch (a->n) {
+        switch (json_array_size(condition)) {
         case 0:
             return COND_FALSE;
 
-        case 1:
-            return (a->elems[0]->type == JSON_FALSE ? COND_FALSE
-                    : a->elems[0]->type == JSON_TRUE ? COND_TRUE
+        case 1: {
+            const struct json *cond = json_array_at(condition, 0);
+
+            return (cond->type == JSON_FALSE ? COND_FALSE
+                    : cond->type == JSON_TRUE ? COND_TRUE
                     : COND_OTHER);
+        }
 
         default:
             return COND_OTHER;
@@ -1387,9 +1389,9 @@  ovsdb_cs_db_parse_lock_notify(struct ovsdb_cs_db *db,
 {
     if (db->lock_name
         && params->type == JSON_ARRAY
-        && json_array(params)->n > 0
-        && json_array(params)->elems[0]->type == JSON_STRING) {
-        const char *lock_name = json_string(json_array(params)->elems[0]);
+        && json_array_size(params) > 0
+        && json_array_at(params, 0)->type == JSON_STRING) {
+        const char *lock_name = json_string(json_array_at(params, 0));
 
         if (!strcmp(db->lock_name, lock_name)) {
             ovsdb_cs_db_update_has_lock(db, new_has_lock);
@@ -1523,12 +1525,12 @@  ovsdb_cs_send_monitor_request(struct ovsdb_cs *cs, struct ovsdb_cs_db *db,
                 ovs_assert(mr->type == JSON_ARRAY);
 
                 struct json *mr0;
-                if (json_array(mr)->n == 0) {
+                if (json_array_size(mr) == 0) {
                     mr0 = json_object_create();
                     json_object_put(mr0, "columns", json_array_create_empty());
                     json_array_add(mr, mr0);
                 } else {
-                    mr0 = json_array(mr)->elems[0];
+                    mr0 = CONST_CAST(struct json *, json_array_at(mr, 0));
                 }
                 ovs_assert(mr0->type == JSON_OBJECT);
 
@@ -1585,21 +1587,21 @@  ovsdb_cs_db_parse_monitor_reply(struct ovsdb_cs_db *db,
     const struct json *table_updates;
     bool clear;
     if (version == 3) {
-        if (result->type != JSON_ARRAY || result->array.n != 3
-            || (result->array.elems[0]->type != JSON_TRUE &&
-                result->array.elems[0]->type != JSON_FALSE)
-            || result->array.elems[1]->type != JSON_STRING
+        if (result->type != JSON_ARRAY || json_array_size(result) != 3
+            || (json_array_at(result, 0)->type != JSON_TRUE &&
+                json_array_at(result, 0)->type != JSON_FALSE)
+            || json_array_at(result, 1)->type != JSON_STRING
             || !uuid_from_string(&db->last_id,
-                                 json_string(result->array.elems[1]))) {
+                                 json_string(json_array_at(result, 1)))) {
             struct ovsdb_error *error = ovsdb_syntax_error(
                 result, NULL, "bad monitor_cond_since reply format");
             log_parse_update_error(error);
             return;
         }
 
-        bool found = json_boolean(result->array.elems[0]);
+        bool found = json_boolean(json_array_at(result, 0));
         clear = !found;
-        table_updates = result->array.elems[2];
+        table_updates = json_array_at(result, 2);
     } else {
         clear = true;
         table_updates = result;
@@ -1626,7 +1628,7 @@  ovsdb_cs_db_parse_update_rpc(struct ovsdb_cs_db *db,
 
     struct json *params = msg->params;
     int n = version == 3 ? 3 : 2;
-    if (params->type != JSON_ARRAY || params->array.n != n) {
+    if (params->type != JSON_ARRAY || json_array_size(params) != n) {
         struct ovsdb_error *error = ovsdb_syntax_error(
             params, NULL, "%s must be an array with %u elements.",
             msg->method, n);
@@ -1634,12 +1636,12 @@  ovsdb_cs_db_parse_update_rpc(struct ovsdb_cs_db *db,
         return false;
     }
 
-    if (!json_equal(params->array.elems[0], db->monitor_id)) {
+    if (!json_equal(json_array_at(params, 0), db->monitor_id)) {
         return false;
     }
 
     if (version == 3) {
-        const char *last_id = json_string(params->array.elems[1]);
+        const char *last_id = json_string(json_array_at(params, 1));
         if (!uuid_from_string(&db->last_id, last_id)) {
             struct ovsdb_error *error = ovsdb_syntax_error(
                 params, NULL, "Last-id %s is not in UUID format.", last_id);
@@ -1648,7 +1650,8 @@  ovsdb_cs_db_parse_update_rpc(struct ovsdb_cs_db *db,
         }
     }
 
-    struct json *table_updates = params->array.elems[version == 3 ? 2 : 1];
+    const struct json *table_updates = json_array_at(params,
+                                                     version == 3 ? 2 : 1);
     ovsdb_cs_db_add_update(db, table_updates, version, false, false);
     return true;
 }
@@ -1661,8 +1664,8 @@  ovsdb_cs_handle_monitor_canceled(struct ovsdb_cs *cs,
     if (msg->type != JSONRPC_NOTIFY
         || strcmp(msg->method, "monitor_canceled")
         || msg->params->type != JSON_ARRAY
-        || msg->params->array.n != 1
-        || !json_equal(msg->params->array.elems[0], db->monitor_id)) {
+        || json_array_size(msg->params) != 1
+        || !json_equal(json_array_at(msg->params, 0), db->monitor_id)) {
         return false;
     }
 
diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c
index d38eee5d0..64b6a9d51 100644
--- a/lib/ovsdb-data.c
+++ b/lib/ovsdb-data.c
@@ -262,16 +262,16 @@  unwrap_json(const struct json *json, const char *name,
             enum json_type value_type, const struct json **value)
 {
     if (json->type != JSON_ARRAY
-        || json->array.n != 2
-        || json->array.elems[0]->type != JSON_STRING
-        || (name && strcmp(json_string(json->array.elems[0]), name))
-        || json->array.elems[1]->type != value_type)
+        || json_array_size(json) != 2
+        || json_array_at(json, 0)->type != JSON_STRING
+        || (name && strcmp(json_string(json_array_at(json, 0)), name))
+        || json_array_at(json, 1)->type != value_type)
     {
         *value = NULL;
         return ovsdb_syntax_error(json, NULL, "expected [\"%s\", <%s>]", name,
                                   json_type_to_string(value_type));
     }
-    *value = json->array.elems[1];
+    *value = json_array_at(json, 1);
     return NULL;
 }
 
@@ -279,11 +279,11 @@  static struct ovsdb_error *
 parse_json_pair(const struct json *json,
                 const struct json **elem0, const struct json **elem1)
 {
-    if (json->type != JSON_ARRAY || json->array.n != 2) {
+    if (json->type != JSON_ARRAY || json_array_size(json) != 2) {
         return ovsdb_syntax_error(json, NULL, "expected 2-element array");
     }
-    *elem0 = json->array.elems[0];
-    *elem1 = json->array.elems[1];
+    *elem0 = json_array_at(json, 0);
+    *elem1 = json_array_at(json, 1);
     return NULL;
 }
 
@@ -1276,9 +1276,9 @@  ovsdb_datum_from_json__(struct ovsdb_datum *datum,
 
     if (ovsdb_type_is_map(type)
         || (json->type == JSON_ARRAY
-            && json->array.n > 0
-            && json->array.elems[0]->type == JSON_STRING
-            && !strcmp(json_string(json->array.elems[0]), "set"))) {
+            && json_array_size(json) > 0
+            && json_array_at(json, 0)->type == JSON_STRING
+            && !strcmp(json_string(json_array_at(json, 0)), "set"))) {
         bool is_map = ovsdb_type_is_map(type);
         const char *class = is_map ? "map" : "set";
         const struct json *inner;
@@ -1290,7 +1290,7 @@  ovsdb_datum_from_json__(struct ovsdb_datum *datum,
             return error;
         }
 
-        n = inner->array.n;
+        n = json_array_size(inner);
         if (n < type->n_min || n > type->n_max) {
             if (type->n_min == 1 && type->n_max == 1) {
                 return ovsdb_syntax_error(json, NULL, "%s must have exactly "
@@ -1309,7 +1309,7 @@  ovsdb_datum_from_json__(struct ovsdb_datum *datum,
         datum->values = is_map ? xmalloc(n * sizeof *datum->values) : NULL;
         datum->refcnt = NULL;
         for (i = 0; i < n; i++) {
-            const struct json *element = inner->array.elems[i];
+            const struct json *element = json_array_at(inner, i);
             const struct json *key = NULL;
             const struct json *value = NULL;
 
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index cef4a337b..070a2124d 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -2862,11 +2862,11 @@  substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
         struct uuid uuid;
         size_t i;
 
-        if (json->array.n == 2
-            && json->array.elems[0]->type == JSON_STRING
-            && json->array.elems[1]->type == JSON_STRING
-            && !strcmp(json_string(json->array.elems[0]), "uuid")
-            && uuid_from_string(&uuid, json_string(json->array.elems[1]))) {
+        if (json_array_size(json) == 2
+            && json_array_at(json, 0)->type == JSON_STRING
+            && json_array_at(json, 1)->type == JSON_STRING
+            && !strcmp(json_string(json_array_at(json, 0)), "uuid")
+            && uuid_from_string(&uuid, json_string(json_array_at(json, 1)))) {
             const struct ovsdb_idl_row *row;
 
             row = ovsdb_idl_txn_get_row(txn, &uuid);
@@ -2882,9 +2882,11 @@  substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
             }
         }
 
-        for (i = 0; i < json->array.n; i++) {
-            json->array.elems[i] = substitute_uuids(json->array.elems[i],
-                                                      txn);
+        for (i = 0; i < json_array_size(json); i++) {
+            json_array_set(
+                json, i,
+                substitute_uuids(
+                    CONST_CAST(struct json *, json_array_at(json, i)), txn));
         }
     } else if (json->type == JSON_OBJECT) {
         struct shash_node *node;
@@ -3318,7 +3320,7 @@  ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)
 
                 insert = xmalloc(sizeof *insert);
                 insert->dummy = row->uuid;
-                insert->op_index = operations->array.n - 1;
+                insert->op_index = json_array_size(operations) - 1;
                 uuid_zero(&insert->real);
                 hmap_insert(&txn->inserted_rows, &insert->hmap_node,
                             uuid_hash(&insert->dummy));
@@ -3388,7 +3390,7 @@  ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)
     /* Add increment. */
     if (txn->inc_table && (any_updates || txn->inc_force)) {
         any_updates = true;
-        txn->inc_index = operations->array.n - 1;
+        txn->inc_index = json_array_size(operations) - 1;
 
         struct json *op = json_object_create();
         json_object_put_string(op, "op", "mutate");
@@ -3903,21 +3905,21 @@  check_json_type(const struct json *json, enum json_type type, const char *name)
 
 static bool
 ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn,
-                                const struct json_array *results)
+                                const struct json *results)
 {
-    struct json *count, *rows, *row, *column;
+    const struct json *count, *rows, *row, *column;
     struct shash *mutate, *select;
 
-    if (txn->inc_index + 2 > results->n) {
+    if (txn->inc_index + 2 > json_array_size(results)) {
         VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations "
                      "for increment (has %"PRIuSIZE", needs %u)",
-                     results->n, txn->inc_index + 2);
+                     json_array_size(results), txn->inc_index + 2);
         return false;
     }
 
     /* We know that this is a JSON object because the loop in
      * ovsdb_idl_txn_process_reply() checked. */
-    mutate = json_object(results->elems[txn->inc_index]);
+    mutate = json_object(json_array_at(results, txn->inc_index));
     count = shash_find_data(mutate, "count");
     if (!check_json_type(count, JSON_INTEGER, "\"mutate\" reply \"count\"")) {
         return false;
@@ -3929,18 +3931,18 @@  ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn,
         return false;
     }
 
-    select = json_object(results->elems[txn->inc_index + 1]);
+    select = json_object(json_array_at(results, txn->inc_index + 1));
     rows = shash_find_data(select, "rows");
     if (!check_json_type(rows, JSON_ARRAY, "\"select\" reply \"rows\"")) {
         return false;
     }
-    if (rows->array.n != 1) {
+    if (json_array_size(rows) != 1) {
         VLOG_WARN_RL(&syntax_rl, "\"select\" reply \"rows\" has %"PRIuSIZE" elements "
                      "instead of 1",
-                     rows->array.n);
+                     json_array_size(rows));
         return false;
     }
-    row = rows->array.elems[0];
+    row = json_array_at(rows, 0);
     if (!check_json_type(row, JSON_OBJECT, "\"select\" reply row")) {
         return false;
     }
@@ -3955,7 +3957,7 @@  ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn,
 
 static bool
 ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert,
-                                   const struct json_array *results)
+                                   const struct json *results)
 {
     static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT;
     struct ovsdb_error *error;
@@ -3963,16 +3965,16 @@  ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert,
     union ovsdb_atom uuid;
     struct shash *reply;
 
-    if (insert->op_index >= results->n) {
+    if (insert->op_index >= json_array_size(results)) {
         VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations "
                      "for insert (has %"PRIuSIZE", needs %u)",
-                     results->n, insert->op_index);
+                     json_array_size(results), insert->op_index);
         return false;
     }
 
     /* We know that this is a JSON object because the loop in
      * ovsdb_idl_txn_process_reply() checked. */
-    reply = json_object(results->elems[insert->op_index]);
+    reply = json_object(json_array_at(results, insert->op_index));
     json_uuid = shash_find_data(reply, "uuid");
     if (!check_json_type(json_uuid, JSON_ARRAY, "\"insert\" reply \"uuid\"")) {
         return false;
@@ -4019,14 +4021,15 @@  ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl,
         status = TXN_ERROR;
         ovsdb_idl_txn_set_error_json(txn, msg->result);
     } else {
-        struct json_array *ops = &msg->result->array;
+        const struct json *ops = msg->result;
         int hard_errors = 0;
         int soft_errors = 0;
         int lock_errors = 0;
-        size_t i;
+        size_t i, n;
 
-        for (i = 0; i < ops->n; i++) {
-            struct json *op = ops->elems[i];
+        n = json_array_size(ops);
+        for (i = 0; i < n; i++) {
+            const struct json *op = json_array_at(ops, i);
 
             if (op->type == JSON_NULL) {
                 /* This isn't an error in itself but indicates that some prior
diff --git a/lib/unixctl.c b/lib/unixctl.c
index c060e8659..4fd150959 100644
--- a/lib/unixctl.c
+++ b/lib/unixctl.c
@@ -385,7 +385,7 @@  process_command(struct unixctl_conn *conn, struct jsonrpc_msg *request)
     char *error = NULL;
 
     struct unixctl_command *command;
-    struct json_array *params;
+    const struct json *params;
 
     COVERAGE_INC(unixctl_received);
     conn->request_id = json_clone(request->id);
@@ -399,30 +399,32 @@  process_command(struct unixctl_conn *conn, struct jsonrpc_msg *request)
         free(id_s);
     }
 
-    params = json_array(request->params);
+    params = request->params;
     command = shash_find_data(&commands, request->method);
     if (!command) {
         error = xasprintf("\"%s\" is not a valid command (use "
                           "\"list-commands\" to see a list of valid commands)",
                           request->method);
-    } else if (params->n < command->min_args) {
+    } else if (json_array_size(params) < command->min_args) {
         error = xasprintf("\"%s\" command requires at least %d arguments",
                           request->method, command->min_args);
-    } else if (params->n > command->max_args) {
+    } else if (json_array_size(params) > command->max_args) {
         error = xasprintf("\"%s\" command takes at most %d arguments",
                           request->method, command->max_args);
     } else {
         struct svec argv = SVEC_EMPTY_INITIALIZER;
-        int  i;
+        int  i, n = json_array_size(params);
 
         svec_add(&argv, request->method);
-        for (i = 0; i < params->n; i++) {
-            if (params->elems[i]->type != JSON_STRING) {
+        for (i = 0; i < n; i++) {
+            const struct json *elem = json_array_at(params, i);
+
+            if (elem->type != JSON_STRING) {
                 error = xasprintf("\"%s\" command has non-string argument",
                                   request->method);
                 break;
             }
-            svec_add(&argv, json_string(params->elems[i]));
+            svec_add(&argv, json_string(elem));
         }
         svec_terminate(&argv);
 
diff --git a/ovsdb/column.c b/ovsdb/column.c
index 37cda730c..f4dc5bea6 100644
--- a/ovsdb/column.c
+++ b/ovsdb/column.c
@@ -158,22 +158,25 @@  ovsdb_column_set_from_json(const struct json *json,
         return NULL;
     } else {
         struct ovsdb_error *error = NULL;
-        size_t i;
+        size_t i, n;
 
         if (json->type != JSON_ARRAY) {
             goto error;
         }
 
         /* XXX this is O(n**2) */
-        for (i = 0; i < json->array.n; i++) {
+        n = json_array_size(json);
+        for (i = 0; i < n; i++) {
             const struct ovsdb_column *column;
+            const struct json *elem;
             const char *s;
 
-            if (json->array.elems[i]->type != JSON_STRING) {
+            elem = json_array_at(json, i);
+            if (elem->type != JSON_STRING) {
                 goto error;
             }
 
-            s = json_string(json->array.elems[i]);
+            s = json_string(elem);
             column = shash_find_data(&schema->columns, s);
             if (!column) {
                 error = ovsdb_syntax_error(json, NULL, "%s is not a valid "
diff --git a/ovsdb/condition.c b/ovsdb/condition.c
index 4911fbf59..500fce761 100644
--- a/ovsdb/condition.c
+++ b/ovsdb/condition.c
@@ -35,7 +35,6 @@  ovsdb_clause_from_json(const struct ovsdb_table_schema *ts,
                        struct ovsdb_symbol_table *symtab,
                        struct ovsdb_clause *clause)
 {
-    const struct json_array *array;
     struct ovsdb_error *error;
     const char *function_name;
     const char *column_name;
@@ -57,14 +56,13 @@  ovsdb_clause_from_json(const struct ovsdb_table_schema *ts,
     }
 
     if (json->type != JSON_ARRAY
-        || json->array.n != 3
-        || json->array.elems[0]->type != JSON_STRING
-        || json->array.elems[1]->type != JSON_STRING) {
+        || json_array_size(json) != 3
+        || json_array_at(json, 0)->type != JSON_STRING
+        || json_array_at(json, 1)->type != JSON_STRING) {
         return ovsdb_syntax_error(json, NULL, "Parse error in condition.");
     }
-    array = json_array(json);
 
-    column_name = json_string(array->elems[0]);
+    column_name = json_string(json_array_at(json, 0));
     clause->column = ovsdb_table_schema_get_column(ts, column_name);
     if (!clause->column) {
         return ovsdb_syntax_error(json, "unknown column",
@@ -74,7 +72,7 @@  ovsdb_clause_from_json(const struct ovsdb_table_schema *ts,
     clause->index = clause->column->index;
     type = clause->column->type;
 
-    function_name = json_string(array->elems[1]);
+    function_name = json_string(json_array_at(json, 1));
     error = ovsdb_function_from_string(function_name, &clause->function);
     if (error) {
         return error;
@@ -127,7 +125,8 @@  ovsdb_clause_from_json(const struct ovsdb_table_schema *ts,
     case OVSDB_F_FALSE:
         OVS_NOT_REACHED();
     }
-    return ovsdb_datum_from_json(&clause->arg, &type, array->elems[2], symtab);
+    return ovsdb_datum_from_json(&clause->arg, &type,
+                                 json_array_at(json, 2), symtab);
 }
 
 static void
@@ -246,15 +245,14 @@  ovsdb_condition_from_json(const struct ovsdb_table_schema *ts,
                           struct ovsdb_symbol_table *symtab,
                           struct ovsdb_condition *cnd)
 {
-    const struct json_array *array = json_array(json);
-    size_t i;
+    size_t i, n = json_array_size(json);
 
     ovsdb_condition_init(cnd);
-    cnd->clauses = xmalloc(array->n * sizeof *cnd->clauses);
+    cnd->clauses = xmalloc(n * sizeof *cnd->clauses);
 
-    for (i = 0; i < array->n; i++) {
+    for (i = 0; i < n; i++) {
         struct ovsdb_error *error;
-        error = ovsdb_clause_from_json(ts, array->elems[i], symtab,
+        error = ovsdb_clause_from_json(ts, json_array_at(json, i), symtab,
                                        &cnd->clauses[i]);
         if (error) {
             ovsdb_condition_destroy(cnd);
diff --git a/ovsdb/execution.c b/ovsdb/execution.c
index 756129da4..38cdcd166 100644
--- a/ovsdb/execution.c
+++ b/ovsdb/execution.c
@@ -126,9 +126,9 @@  ovsdb_execute_compose(struct ovsdb *db, const struct ovsdb_session *session,
         *forwarding_needed = false;
     }
     if (params->type != JSON_ARRAY
-        || !params->array.n
-        || params->array.elems[0]->type != JSON_STRING
-        || strcmp(json_string(params->array.elems[0]), db->schema->name)) {
+        || !json_array_size(params)
+        || json_array_at(params, 0)->type != JSON_STRING
+        || strcmp(json_string(json_array_at(params, 0)), db->schema->name)) {
         if (params->type != JSON_ARRAY) {
             error = ovsdb_syntax_error(params, NULL, "array expected");
         } else {
@@ -152,10 +152,10 @@  ovsdb_execute_compose(struct ovsdb *db, const struct ovsdb_session *session,
     results = NULL;
 
     results = json_array_create_empty();
-    n_operations = params->array.n - 1;
+    n_operations = json_array_size(params) - 1;
     error = NULL;
     for (i = 1; i <= n_operations; i++) {
-        struct json *operation = params->array.elems[i];
+        const struct json *operation = json_array_at(params, i);
         struct ovsdb_error *parse_error;
         struct ovsdb_parser parser;
         struct json *result;
@@ -219,7 +219,7 @@  ovsdb_execute_compose(struct ovsdb *db, const struct ovsdb_session *session,
         }
         json_array_add(results, result);
     }
-    while (json_array(results)->n < n_operations) {
+    while (json_array_size(results) < n_operations) {
         json_array_add(results, json_null_create());
     }
 
@@ -745,7 +745,7 @@  ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
     struct ovsdb_error *error;
     struct wait_auxdata aux;
     long long int timeout_msec = 0;
-    size_t i;
+    size_t i, n;
 
     timeout = ovsdb_parser_member(parser, "timeout", OP_INTEGER | OP_OPTIONAL);
     where = ovsdb_parser_member(parser, "where", OP_ARRAY);
@@ -786,11 +786,13 @@  ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
     if (!error) {
         /* Parse "rows" into 'expected'. */
         ovsdb_row_hash_init(&expected, &columns);
-        for (i = 0; i < rows->array.n; i++) {
+
+        n = json_array_size(rows);
+        for (i = 0; i < n; i++) {
             struct ovsdb_row *row;
 
             row = ovsdb_row_create(table);
-            error = ovsdb_row_from_json(row, rows->array.elems[i], x->symtab,
+            error = ovsdb_row_from_json(row, json_array_at(rows, i), x->symtab,
                                         NULL, false);
             if (error) {
                 ovsdb_row_destroy(row);
diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c
index 50fb2c7f1..175f7bb4e 100644
--- a/ovsdb/jsonrpc-server.c
+++ b/ovsdb/jsonrpc-server.c
@@ -107,7 +107,7 @@  static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cond_change(
     const struct json *request_id);
 static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel(
     struct ovsdb_jsonrpc_session *,
-    struct json_array *params,
+    const struct json *params,
     const struct json *request_id);
 static void ovsdb_jsonrpc_monitor_preremove_db(struct ovsdb_jsonrpc_session *,
                                                struct ovsdb *);
@@ -887,20 +887,21 @@  ovsdb_jsonrpc_lookup_db(const struct ovsdb_jsonrpc_session *s,
                         const struct jsonrpc_msg *request,
                         struct jsonrpc_msg **replyp)
 {
-    struct json_array *params;
+    const struct json *params;
     struct ovsdb_error *error;
     const char *db_name;
     struct ovsdb *db;
 
-    params = json_array(request->params);
-    if (!params->n || params->elems[0]->type != JSON_STRING) {
+    params = request->params;
+    if (!json_array_size(params)
+        || json_array_at(params, 0)->type != JSON_STRING) {
         error = ovsdb_syntax_error(
             request->params, NULL,
             "%s request params must begin with <db-name>", request->method);
         goto error;
     }
 
-    db_name = json_string(params->elems[0]);
+    db_name = json_string(json_array_at(params, 0));
     db = shash_find_data(&s->up.server->dbs, db_name);
     if (!db) {
         error = ovsdb_syntax_error(
@@ -932,18 +933,19 @@  static struct ovsdb_error *
 ovsdb_jsonrpc_session_parse_lock_name(const struct jsonrpc_msg *request,
                                       const char **lock_namep)
 {
-    const struct json_array *params;
+    const struct json *params = request->params;
+    const struct json *elem = json_array_at(params, 0);
 
-    params = json_array(request->params);
-    if (params->n != 1 || params->elems[0]->type != JSON_STRING ||
-        !ovsdb_parser_is_id(json_string(params->elems[0]))) {
+    if (json_array_size(params) != 1
+        || elem->type != JSON_STRING
+        || !ovsdb_parser_is_id(json_string(elem))) {
         *lock_namep = NULL;
-        return ovsdb_syntax_error(request->params, NULL,
+        return ovsdb_syntax_error(params, NULL,
                                   "%s request params must be <id>",
                                   request->method);
     }
 
-    *lock_namep = json_string(params->elems[0]);
+    *lock_namep = json_string(elem);
     return NULL;
 }
 
@@ -1088,14 +1090,14 @@  static struct jsonrpc_msg *
 ovsdb_jsonrpc_session_set_db_change_aware(struct ovsdb_jsonrpc_session *s,
                                           const struct jsonrpc_msg *request)
 {
-    const struct json_array *params = json_array(request->params);
-    if (params->n != 1
-        || (params->elems[0]->type != JSON_TRUE &&
-            params->elems[0]->type != JSON_FALSE)) {
+    const struct json *params = request->params;
+    if (json_array_size(params) != 1
+        || (json_array_at(params, 0)->type != JSON_TRUE &&
+            json_array_at(params, 0)->type != JSON_FALSE)) {
         return syntax_error_reply(request, "true or false parameter expected");
     }
 
-    s->db_change_aware = json_boolean(params->elems[0]);
+    s->db_change_aware = json_boolean(json_array_at(params, 0));
     return jsonrpc_create_reply(json_object_create(), request->id);
 }
 
@@ -1132,8 +1134,7 @@  ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
         reply = ovsdb_jsonrpc_monitor_cond_change(s, request->params,
                                                   request->id);
     } else if (!strcmp(request->method, "monitor_cancel")) {
-        reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params),
-                                             request->id);
+        reply = ovsdb_jsonrpc_monitor_cancel(s, request->params, request->id);
     } else if (!strcmp(request->method, "get_schema")) {
         struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply);
         if (db && !reply) {
@@ -1183,11 +1184,11 @@  ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
 static void
 execute_cancel(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request)
 {
-    if (json_array(request->params)->n == 1) {
+    if (json_array_size(request->params) == 1) {
         struct ovsdb_jsonrpc_trigger *t;
-        struct json *id;
+        const struct json *id;
 
-        id = request->params->array.elems[0];
+        id = json_array_at(request->params, 0);
         t = ovsdb_jsonrpc_trigger_find(s, id, json_hash(id, 0));
         if (t) {
             ovsdb_jsonrpc_trigger_complete(t);
@@ -1431,23 +1432,25 @@  ovsdb_jsonrpc_parse_monitor_request(
 
     ovsdb_monitor_table_add_select(dbmon, table, select);
     if (columns) {
-        size_t i;
+        size_t i, n;
 
         if (columns->type != JSON_ARRAY) {
             return ovsdb_syntax_error(columns, NULL,
                                       "array of column names expected");
         }
 
-        for (i = 0; i < columns->array.n; i++) {
+        n = json_array_size(columns);
+        for (i = 0; i < n; i++) {
+            const struct json *elem = json_array_at(columns, i);
             const struct ovsdb_column *column;
             const char *s;
 
-            if (columns->array.elems[i]->type != JSON_STRING) {
+            if (elem->type != JSON_STRING) {
                 return ovsdb_syntax_error(columns, NULL,
                                           "array of column names expected");
             }
 
-            s = json_string(columns->array.elems[i]);
+            s = json_string(elem);
             column = shash_find_data(&table->schema->columns, s);
             if (!column) {
                 return ovsdb_syntax_error(columns, NULL, "%s is not a valid "
@@ -1493,20 +1496,20 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
 {
     ovs_assert(db);
 
+    const struct json *monitor_id, *monitor_requests;
     struct ovsdb_jsonrpc_monitor *m = NULL;
     struct ovsdb_monitor *dbmon = NULL;
-    struct json *monitor_id, *monitor_requests;
     struct ovsdb_error *error = NULL;
     struct shash_node *node;
     struct json *json;
 
-    if ((version == OVSDB_MONITOR_V2 && json_array(params)->n != 3) ||
-        (version == OVSDB_MONITOR_V3 && json_array(params)->n != 4)) {
+    if ((version == OVSDB_MONITOR_V2 && json_array_size(params) != 3) ||
+        (version == OVSDB_MONITOR_V3 && json_array_size(params) != 4)) {
         error = ovsdb_syntax_error(params, NULL, "invalid parameters");
         goto error;
     }
-    monitor_id = params->array.elems[1];
-    monitor_requests = params->array.elems[2];
+    monitor_id = json_array_at(params, 1);
+    monitor_requests = json_array_at(params, 2);
     if (monitor_requests->type != JSON_OBJECT) {
         error = ovsdb_syntax_error(monitor_requests, NULL,
                                    "monitor-requests must be object");
@@ -1532,7 +1535,7 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
     SHASH_FOR_EACH (node, json_object(monitor_requests)) {
         const struct ovsdb_table *table;
         const struct json *mr_value;
-        size_t i;
+        size_t i, n;
 
         table = ovsdb_get_table(m->db, node->name);
         if (!table) {
@@ -1546,13 +1549,14 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
         /* Parse columns. */
         mr_value = node->data;
         if (mr_value->type == JSON_ARRAY) {
-            const struct json_array *array = &mr_value->array;
+            n = json_array_size(mr_value);
+            for (i = 0; i < n; i++) {
+                const struct json *elem = json_array_at(mr_value, i);
 
-            for (i = 0; i < array->n; i++) {
                 error = ovsdb_jsonrpc_parse_monitor_request(m->dbmon,
                                                             table,
                                                             m->condition,
-                                                            array->elems[i]);
+                                                            elem);
                 if (error) {
                     goto error;
                 }
@@ -1583,7 +1587,8 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
 
     bool initial = false;
     if (version == OVSDB_MONITOR_V3) {
-        struct json *last_id = params->array.elems[3];
+        const struct json *last_id = json_array_at(params, 3);
+
         if (last_id->type != JSON_STRING) {
             error = ovsdb_syntax_error(last_id, NULL,
                                        "last-txn-id must be string");
@@ -1661,24 +1666,24 @@  ovsdb_jsonrpc_monitor_cond_change(struct ovsdb_jsonrpc_session *s,
                                   struct json *params,
                                   const struct json *request_id)
 {
-    struct ovsdb_error *error;
+    const struct json *monitor_cond_change_reqs;
     struct ovsdb_jsonrpc_monitor *m;
-    struct json *monitor_cond_change_reqs;
+    struct ovsdb_error *error;
     struct shash_node *node;
 
-    if (json_array(params)->n != 3) {
+    if (json_array_size(params) != 3) {
         error = ovsdb_syntax_error(params, NULL, "invalid parameters");
         goto error;
     }
 
-    m = ovsdb_jsonrpc_monitor_find(s, params->array.elems[0]);
+    m = ovsdb_jsonrpc_monitor_find(s, json_array_at(params, 0));
     if (!m) {
-        error = ovsdb_syntax_error(params->array.elems[0], NULL,
+        error = ovsdb_syntax_error(json_array_at(params, 0), NULL,
                                    "unknown monitor session");
         goto error;
     }
 
-    const struct json *new_monitor_id = params->array.elems[1];
+    const struct json *new_monitor_id = json_array_at(params, 1);
     bool changing_id = !json_equal(m->monitor_id, new_monitor_id);
     if (changing_id && ovsdb_jsonrpc_monitor_find(s, new_monitor_id)) {
         error = ovsdb_syntax_error(new_monitor_id, NULL,
@@ -1686,7 +1691,7 @@  ovsdb_jsonrpc_monitor_cond_change(struct ovsdb_jsonrpc_session *s,
         goto error;
     }
 
-    monitor_cond_change_reqs = params->array.elems[2];
+    monitor_cond_change_reqs = json_array_at(params, 2);
     if (monitor_cond_change_reqs->type != JSON_OBJECT) {
         error =
             ovsdb_syntax_error(NULL, NULL,
@@ -1697,7 +1702,7 @@  ovsdb_jsonrpc_monitor_cond_change(struct ovsdb_jsonrpc_session *s,
     SHASH_FOR_EACH (node, json_object(monitor_cond_change_reqs)) {
         const struct ovsdb_table *table;
         const struct json *mr_value;
-        size_t i;
+        size_t i, n;
 
         table = ovsdb_get_table(m->db, node->name);
         if (!table) {
@@ -1714,11 +1719,10 @@  ovsdb_jsonrpc_monitor_cond_change(struct ovsdb_jsonrpc_session *s,
 
         mr_value = node->data;
         if (mr_value->type == JSON_ARRAY) {
-            const struct json_array *array = &mr_value->array;
-
-            for (i = 0; i < array->n; i++) {
+            n = json_array_size(mr_value);
+            for (i = 0; i < n; i++) {
                 error = ovsdb_jsonrpc_parse_monitor_cond_change_request(
-                                            m, table, array->elems[i]);
+                                    m, table, json_array_at(mr_value, i));
                 if (error) {
                     goto error;
                 }
@@ -1769,16 +1773,16 @@  error:
 
 static struct jsonrpc_msg *
 ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s,
-                             struct json_array *params,
+                             const struct json *params,
                              const struct json *request_id)
 {
-    if (params->n != 1) {
+    if (json_array_size(params) != 1) {
         return jsonrpc_create_error(json_string_create("invalid parameters"),
                                     request_id);
     } else {
         struct ovsdb_jsonrpc_monitor *m;
 
-        m = ovsdb_jsonrpc_monitor_find(s, params->elems[0]);
+        m = ovsdb_jsonrpc_monitor_find(s, json_array_at(params, 0));
         if (!m) {
             return jsonrpc_create_error(json_string_create("unknown monitor"),
                                         request_id);
diff --git a/ovsdb/mutation.c b/ovsdb/mutation.c
index 794560019..f5ca8c886 100644
--- a/ovsdb/mutation.c
+++ b/ovsdb/mutation.c
@@ -79,20 +79,18 @@  ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
                          struct ovsdb_symbol_table *symtab,
                          struct ovsdb_mutation *m)
 {
-    const struct json_array *array;
     struct ovsdb_error *error;
     const char *mutator_name;
     const char *column_name;
 
     if (json->type != JSON_ARRAY
-        || json->array.n != 3
-        || json->array.elems[0]->type != JSON_STRING
-        || json->array.elems[1]->type != JSON_STRING) {
+        || json_array_size(json) != 3
+        || json_array_at(json, 0)->type != JSON_STRING
+        || json_array_at(json, 1)->type != JSON_STRING) {
         return ovsdb_syntax_error(json, NULL, "Parse error in mutation.");
     }
-    array = json_array(json);
 
-    column_name = json_string(array->elems[0]);
+    column_name = json_string(json_array_at(json, 0));
     m->column = ovsdb_table_schema_get_column(ts, column_name);
     if (!m->column) {
         return ovsdb_syntax_error(json, "unknown column",
@@ -107,7 +105,7 @@  ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
 
     ovsdb_type_clone(&m->type, &m->column->type);
 
-    mutator_name = json_string(array->elems[1]);
+    mutator_name = json_string(json_array_at(json, 1));
     error = ovsdb_mutator_from_string(mutator_name, &m->mutator);
     if (error) {
         goto exit;
@@ -129,8 +127,8 @@  ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
         }
         ovsdb_base_type_clear_constraints(&m->type.key);
         m->type.n_min = m->type.n_max = 1;
-        error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
-                                      symtab);
+        error = ovsdb_datum_from_json(&m->arg, &m->type,
+                                      json_array_at(json, 2), symtab);
         break;
 
     case OVSDB_M_INSERT:
@@ -142,16 +140,16 @@  ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
         if (m->mutator == OVSDB_M_DELETE) {
             m->type.n_max = UINT_MAX;
         }
-        error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
-                                      symtab);
+        error = ovsdb_datum_from_json(&m->arg, &m->type,
+                                      json_array_at(json, 2), symtab);
         if (error && ovsdb_type_is_map(&m->type)
             && m->mutator == OVSDB_M_DELETE) {
             ovsdb_error_destroy(error);
             ovsdb_base_type_destroy(&m->type.value);
             m->type.value.enum_ = NULL;
             m->type.value.type = OVSDB_TYPE_VOID;
-            error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
-                                          symtab);
+            error = ovsdb_datum_from_json(&m->arg, &m->type,
+                                          json_array_at(json, 2), symtab);
         }
         break;
 
@@ -179,14 +177,13 @@  ovsdb_mutation_set_from_json(const struct ovsdb_table_schema *ts,
                              struct ovsdb_symbol_table *symtab,
                              struct ovsdb_mutation_set *set)
 {
-    const struct json_array *array = json_array(json);
-    size_t i;
+    size_t i, n = json_array_size(json);
 
-    set->mutations = xmalloc(array->n * sizeof *set->mutations);
+    set->mutations = xmalloc(n * sizeof *set->mutations);
     set->n_mutations = 0;
-    for (i = 0; i < array->n; i++) {
+    for (i = 0; i < n; i++) {
         struct ovsdb_error *error;
-        error = ovsdb_mutation_from_json(ts, array->elems[i], symtab,
+        error = ovsdb_mutation_from_json(ts, json_array_at(json, i), symtab,
                                          &set->mutations[i]);
         if (error) {
             ovsdb_mutation_set_destroy(set);
diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c
index f85057b6c..37cfa8b56 100644
--- a/ovsdb/ovsdb-client.c
+++ b/ovsdb/ovsdb-client.c
@@ -580,7 +580,7 @@  static void
 fetch_dbs(struct jsonrpc *rpc, struct svec *dbs)
 {
     struct jsonrpc_msg *request, *reply;
-    size_t i;
+    size_t i, n;
 
     request = jsonrpc_create_request("list_dbs", json_array_create_empty(),
                                      NULL);
@@ -590,8 +590,9 @@  fetch_dbs(struct jsonrpc *rpc, struct svec *dbs)
         ovs_fatal(0, "list_dbs response is not array");
     }
 
-    for (i = 0; i < reply->result->array.n; i++) {
-        const struct json *name = reply->result->array.elems[i];
+    n = json_array_size(reply->result);
+    for (i = 0; i < n; i++) {
+        const struct json *name = json_array_at(reply->result, i);
 
         if (name->type != JSON_STRING) {
             ovs_fatal(0, "list_dbs response %"PRIuSIZE" is not string", i);
@@ -663,14 +664,14 @@  parse_database_info_reply(const struct jsonrpc_msg *reply, const char *server,
 {
     const struct json *result = reply->result;
     if (result->type != JSON_ARRAY
-        || result->array.n != 1
-        || result->array.elems[0]->type != JSON_OBJECT) {
+        || json_array_size(result) != 1
+        || json_array_at(result, 0)->type != JSON_OBJECT) {
         VLOG_WARN("%s: unexpected reply to _Server request for %s",
                   server, database);
         return NULL;
     }
 
-    const struct json *op_result = result->array.elems[0];
+    const struct json *op_result = json_array_at(result, 0);
     const struct json *rows = shash_find_data(json_object(op_result), "rows");
     if (!rows || rows->type != JSON_ARRAY) {
         VLOG_WARN("%s: missing \"rows\" member in  _Server reply for %s",
@@ -678,8 +679,9 @@  parse_database_info_reply(const struct jsonrpc_msg *reply, const char *server,
         return NULL;
     }
 
-    for (size_t i = 0; i < rows->array.n; i++) {
-        const struct json *row = rows->array.elems[i];
+    size_t n = json_array_size(rows);
+    for (size_t i = 0; i < n; i++) {
+        const struct json *row = json_array_at(rows, i);
         if (row->type != JSON_OBJECT) {
             VLOG_WARN("%s: bad row in  _Server reply for %s",
                       server, database);
@@ -868,11 +870,11 @@  do_transact__(int argc, char *argv[], struct json *transaction)
 {
     struct jsonrpc_msg *request, *reply;
     if (transaction->type != JSON_ARRAY
-        || !transaction->array.n
-        || transaction->array.elems[0]->type != JSON_STRING) {
+        || !json_array_size(transaction)
+        || json_array_at(transaction, 0)->type != JSON_STRING) {
         ovs_fatal(0, "not a valid OVSDB query");
     }
-    const char *db_name = json_string(transaction->array.elems[0]);
+    const char *db_name = json_string(json_array_at(transaction, 0));
 
     struct jsonrpc *rpc;
     char *database = CONST_CAST(char *, db_name);
@@ -915,22 +917,21 @@  do_query(struct jsonrpc *rpc OVS_UNUSED, const char *database OVS_UNUSED,
     struct json *abort_op = json_object_create();
     json_object_put_string(abort_op, "op", "abort");
     json_array_add(transaction, abort_op);
-    size_t abort_idx = transaction->array.n - 2;
+    size_t abort_idx = json_array_size(transaction) - 2;
 
     /* Run query. */
     struct json *result = do_transact__(argc, argv, transaction);
 
     /* If the "abort" operation ended the transaction, remove its result. */
     if (result->type == JSON_ARRAY
-        && result->array.n == abort_idx + 1
-        && result->array.elems[abort_idx]->type == JSON_OBJECT) {
-        struct json *op_result = result->array.elems[abort_idx];
+        && json_array_size(result) == abort_idx + 1
+        && json_array_at(result, abort_idx)->type == JSON_OBJECT) {
+        const struct json *op_result = json_array_at(result, abort_idx);
         struct json *error = shash_find_data(json_object(op_result), "error");
         if (error
             && error->type == JSON_STRING
             && !strcmp(json_string(error), "aborted")) {
-            result->array.n--;
-            json_destroy(op_result);
+            json_destroy(json_array_pop(result));
         }
     }
 
@@ -946,7 +947,7 @@  struct monitored_table {
 };
 
 static void
-monitor_print_row(struct json *row, const char *type, const char *uuid,
+monitor_print_row(const struct json *row, const char *type, const char *uuid,
                   const struct ovsdb_column_set *columns, struct table *t)
 {
     size_t i;
@@ -1024,7 +1025,7 @@  monitor_print_table(struct json *table_update,
 }
 
 static void
-monitor_print(struct json *table_updates,
+monitor_print(const struct json *table_updates,
               const struct monitored_table *mts, size_t n_mts,
               bool initial)
 {
@@ -1048,7 +1049,7 @@  monitor_print(struct json *table_updates,
 }
 
 static void
-monitor2_print_row(struct json *row, const char *type, const char *uuid,
+monitor2_print_row(const struct json *row, const char *type, const char *uuid,
                    const struct ovsdb_column_set *columns, struct table *t)
 {
     if (!strcmp(type, "delete")) {
@@ -1070,8 +1071,8 @@  monitor2_print_row(struct json *row, const char *type, const char *uuid,
 }
 
 static void
-monitor2_print_table(struct json *table_update2,
-                    const struct monitored_table *mt, char *caption)
+monitor2_print_table(const struct json *table_update2,
+                     const struct monitored_table *mt, char *caption)
 {
     const struct ovsdb_table_schema *table = mt->table;
     const struct ovsdb_column_set *columns = &mt->columns;
@@ -1119,7 +1120,7 @@  monitor2_print_table(struct json *table_update2,
 }
 
 static void
-monitor2_print(struct json *table_updates2,
+monitor2_print(const struct json *table_updates2,
                const struct monitored_table *mts, size_t n_mts)
 {
     size_t i;
@@ -1142,28 +1143,28 @@  monitor2_print(struct json *table_updates2,
 }
 
 static void
-monitor3_print(struct json *result,
+monitor3_print(const struct json *result,
                const struct monitored_table *mts, size_t n_mts)
 {
     if (result->type != JSON_ARRAY) {
         ovs_error(0, "<result> is not array");
     }
 
-    if (result->array.n != 3) {
+    if (json_array_size(result) != 3) {
         ovs_error(0, "<result> should have 3 elements, but has %"PRIuSIZE".",
-                  result->array.n);
+                  json_array_size(result));
     }
 
-    bool found = json_boolean(result->array.elems[0]);
-    const char *last_id = json_string(result->array.elems[1]);
+    bool found = json_boolean(json_array_at(result, 0));
+    const char *last_id = json_string(json_array_at(result, 1));
     printf("found: %s, last_id: %s\n", found ? "true" : "false", last_id);
 
-    struct json *table_updates2 = result->array.elems[2];
+    const struct json *table_updates2 = json_array_at(result, 2);
     monitor2_print(table_updates2, mts, n_mts);
 }
 
 static void
-monitor3_notify_print(const char *last_id, struct json *table_updates2,
+monitor3_notify_print(const char *last_id, const struct json *table_updates2,
                       const struct monitored_table *mts, size_t n_mts)
 {
     printf("\nlast_id: %s", last_id);
@@ -1220,7 +1221,7 @@  parse_monitor_columns(char *arg, const char *server, const char *database,
         }
     }
 
-    if (columns_json->array.n == 0) {
+    if (json_array_size(columns_json) == 0) {
         const struct shash_node **nodes;
         size_t i, n;
 
@@ -1520,9 +1521,9 @@  do_monitor__(struct jsonrpc *rpc, const char *database,
                        && !strcmp(msg->method, "update")) {
                 struct json *params = msg->params;
                 if (params->type == JSON_ARRAY
-                    && params->array.n == 2
-                    && params->array.elems[0]->type == JSON_NULL) {
-                    monitor_print(params->array.elems[1], mts, n_mts, false);
+                    && json_array_size(params) == 2
+                    && json_array_at(params, 0)->type == JSON_NULL) {
+                    monitor_print(json_array_at(params, 1), mts, n_mts, false);
                     fflush(stdout);
                 }
             } else if (msg->type == JSONRPC_NOTIFY
@@ -1530,9 +1531,9 @@  do_monitor__(struct jsonrpc *rpc, const char *database,
                        && !strcmp(msg->method, "update2")) {
                 struct json *params = msg->params;
                 if (params->type == JSON_ARRAY
-                    && params->array.n == 2
-                    && params->array.elems[0]->type == JSON_NULL) {
-                    monitor2_print(params->array.elems[1], mts, n_mts);
+                    && json_array_size(params) == 2
+                    && json_array_at(params, 0)->type == JSON_NULL) {
+                    monitor2_print(json_array_at(params, 1), mts, n_mts);
                     fflush(stdout);
                 }
             } else if (msg->type == JSONRPC_NOTIFY
@@ -1540,10 +1541,11 @@  do_monitor__(struct jsonrpc *rpc, const char *database,
                        && !strcmp(msg->method, "update3")) {
                 struct json *params = msg->params;
                 if (params->type == JSON_ARRAY
-                    && params->array.n == 3
-                    && params->array.elems[0]->type == JSON_NULL) {
-                    monitor3_notify_print(json_string(params->array.elems[1]),
-                                          params->array.elems[2], mts, n_mts);
+                    && json_array_size(params) == 3
+                    && json_array_at(params, 0)->type == JSON_NULL) {
+                    monitor3_notify_print(
+                        json_string(json_array_at(params, 1)),
+                        json_array_at(params, 2), mts, n_mts);
                     fflush(stdout);
                 }
             } else if (msg->type == JSONRPC_NOTIFY
@@ -1759,7 +1761,7 @@  compare_columns(const void *a_, const void *b_)
 
 static void
 dump_table(const char *table_name, const struct shash *cols,
-           struct json_array *rows)
+           const struct json *rows)
 {
     const struct ovsdb_column **columns;
     size_t n_columns;
@@ -1769,7 +1771,7 @@  dump_table(const char *table_name, const struct shash *cols,
     struct dump_table_aux aux;
     struct shash_node *node;
     struct table t;
-    size_t x, y;
+    size_t x, y, n;
 
     /* Sort columns by name, for reproducibility. */
     columns = xmalloc(shash_count(cols) * sizeof *columns);
@@ -1783,15 +1785,17 @@  dump_table(const char *table_name, const struct shash *cols,
     qsort(columns, n_columns, sizeof *columns, compare_columns);
 
     /* Extract data from table. */
-    data = xmalloc(rows->n * sizeof *data);
-    for (y = 0; y < rows->n; y++) {
+    n = json_array_size(rows);
+    data = xmalloc(n * sizeof *data);
+    for (y = 0; y < n; y++) {
+        const struct json *elem = json_array_at(rows, y);
         struct shash *row;
 
-        if (rows->elems[y]->type != JSON_OBJECT) {
+        if (elem->type != JSON_OBJECT) {
             ovs_fatal(0,  "row %"PRIuSIZE" in table %s response is not a JSON object: "
-                      "%s", y, table_name, json_to_string(rows->elems[y], 0));
+                      "%s", y, table_name, json_to_string(elem, 0));
         }
-        row = json_object(rows->elems[y]);
+        row = json_object(elem);
 
         data[y] = xmalloc(n_columns * sizeof **data);
         for (x = 0; x < n_columns; x++) {
@@ -1810,7 +1814,7 @@  dump_table(const char *table_name, const struct shash *cols,
     aux.data = data;
     aux.columns = columns;
     aux.n_columns = n_columns;
-    sort(rows->n, compare_rows, swap_rows, &aux);
+    sort(n, compare_rows, swap_rows, &aux);
 
     /* Add column headings. */
     table_init(&t);
@@ -1820,7 +1824,7 @@  dump_table(const char *table_name, const struct shash *cols,
     }
 
     /* Print rows. */
-    for (y = 0; y < rows->n; y++) {
+    for (y = 0; y < n; y++) {
         table_add_row(&t);
         for (x = 0; x < n_columns; x++) {
             struct cell *cell = table_add_cell(&t);
@@ -1911,13 +1915,13 @@  do_dump(struct jsonrpc *rpc, const char *database,
 
     /* Print database contents. */
     if (reply->result->type != JSON_ARRAY
-        || reply->result->array.n != n_tables) {
+        || json_array_size(reply->result) != n_tables) {
         ovs_fatal(0, "reply is not array of %"PRIuSIZE" elements: %s",
                   n_tables, json_to_string(reply->result, 0));
     }
     for (i = 0; i < n_tables; i++) {
         const struct ovsdb_table_schema *ts = tables[i]->data;
-        const struct json *op_result = reply->result->array.elems[i];
+        const struct json *op_result = json_array_at(reply->result, i);
         struct json *rows;
 
         if (op_result->type != JSON_OBJECT
@@ -1929,9 +1933,9 @@  do_dump(struct jsonrpc *rpc, const char *database,
         }
 
         if (argc > 1) {
-            dump_table(tables[i]->name, &custom_columns, &rows->array);
+            dump_table(tables[i]->name, &custom_columns, rows);
         } else {
-            dump_table(tables[i]->name, &ts->columns, &rows->array);
+            dump_table(tables[i]->name, &ts->columns, rows);
         }
     }
 
@@ -2025,7 +2029,7 @@  do_backup(struct jsonrpc *rpc, const char *database,
 
     /* Print database transaction record. */
     if (reply->result->type != JSON_ARRAY
-        || reply->result->array.n != shash_count(&schema->tables)) {
+        || json_array_size(reply->result) != shash_count(&schema->tables)) {
         ovs_fatal(0, "reply is not array of %"PRIuSIZE" elements: %s",
                   shash_count(&schema->tables),
                   json_to_string(reply->result, 0));
@@ -2036,7 +2040,7 @@  do_backup(struct jsonrpc *rpc, const char *database,
     SHASH_FOR_EACH (node, &schema->tables) {
         const char *table_name = node->name;
         const struct ovsdb_table_schema *table = node->data;
-        const struct json *op_result = reply->result->array.elems[i++];
+        const struct json *op_result = json_array_at(reply->result, i++);
         struct json *rows;
 
         if (op_result->type != JSON_OBJECT
@@ -2047,13 +2051,14 @@  do_backup(struct jsonrpc *rpc, const char *database,
                       table->name, json_to_string(op_result, 0));
         }
 
-        if (!rows->array.n) {
+        size_t n = json_array_size(rows);
+        if (!n) {
             continue;
         }
 
         struct json *output_rows = json_object_create();
-        for (size_t j = 0; j < rows->array.n; j++) {
-            struct json *row = rows->array.elems[j];
+        for (size_t j = 0; j < n; j++) {
+            const struct json *row = json_array_at(rows, j);
             if (row->type != JSON_OBJECT) {
                 ovs_fatal(0, "%s table reply row is not an object: %s",
                           table_name, json_to_string(row, 0));
@@ -2092,8 +2097,8 @@  check_transaction_reply(struct jsonrpc_msg *reply)
     if (reply->result->type != JSON_ARRAY) {
         ovs_fatal(0, "result is not array");
     }
-    for (size_t i = 0; i < json_array(reply->result)->n; i++) {
-        struct json *json = json_array(reply->result)->elems[i];
+    for (size_t i = 0; i < json_array_size(reply->result); i++) {
+        const struct json *json = json_array_at(reply->result, i);
         if (json->type != JSON_OBJECT) {
             ovs_fatal(0, "result array element is not object");
         }
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index a247ae8f0..6edcbd14b 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -3032,16 +3032,18 @@  db_config_from_json(const char *name, const struct json *json)
         sync_exclude = ovsdb_parser_member(&parser, "exclude-tables",
                                            OP_ARRAY | OP_OPTIONAL);
         if (sync_exclude) {
-            const struct json_array *exclude = json_array(sync_exclude);
             struct sset set = SSET_INITIALIZER(&set);
+            size_t n = json_array_size(sync_exclude);
 
-            for (size_t i = 0; i < exclude->n; i++) {
-                if (exclude->elems[i]->type != JSON_STRING) {
+            for (size_t i = 0; i < n; i++) {
+                const struct json *exclude = json_array_at(sync_exclude, i);
+
+                if (exclude->type != JSON_STRING) {
                     ovsdb_parser_raise_error(&parser,
                         "'exclude-tables' must contain strings");
                     break;
                 }
-                sset_add(&set, json_string(exclude->elems[i]));
+                sset_add(&set, json_string(exclude));
             }
             conf->ab.sync_exclude = sset_join(&set, ",", "");
             sset_destroy(&set);
diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c
index 9051020b3..ed35cf6e6 100644
--- a/ovsdb/ovsdb-tool.c
+++ b/ovsdb/ovsdb-tool.c
@@ -874,12 +874,12 @@  print_data(const char *prefix, const struct json *data,
         return;
     }
 
-    if (json_array(data)->n != 2) {
+    if (json_array_size(data) != 2) {
         printf(" ***invalid data***\n");
         return;
     }
 
-    const struct json *schema_json = json_array(data)->elems[0];
+    const struct json *schema_json = json_array_at(data, 0);
     if (schema_json->type != JSON_NULL) {
         struct ovsdb_schema *schema;
 
@@ -891,7 +891,7 @@  print_data(const char *prefix, const struct json *data,
         *schemap = schema;
     }
 
-    print_change_record(json_array(data)->elems[1], *schemap, names);
+    print_change_record(json_array_at(data, 1), *schemap, names);
 }
 
 static void
@@ -977,12 +977,11 @@  raft_header_to_standalone_log(const struct raft_header *h,
     if (h->snap_index) {
         const struct json *data = raft_entry_get_parsed_data(&h->snap);
 
-        if (!data || json_array(data)->n != 2) {
+        if (!data || json_array_size(data) != 2) {
             ovs_fatal(0, "Incorrect raft header data array length");
         }
 
-        struct json_array *pa = json_array(data);
-        struct json *schema_json = pa->elems[0];
+        const struct json *schema_json = json_array_at(data, 0);
         struct ovsdb_error *error = NULL;
 
         if (schema_json->type != JSON_NULL) {
@@ -993,7 +992,8 @@  raft_header_to_standalone_log(const struct raft_header *h,
         }
 
         if (!error) {
-            struct json *data_json = pa->elems[1];
+            const struct json *data_json = json_array_at(data, 1);
+
             if (!data_json || data_json->type != JSON_OBJECT) {
                 ovs_fatal(0, "Invalid raft header data");
             }
@@ -1014,14 +1014,15 @@  raft_record_to_standalone_log(const char *db_file_name,
         if (!r->entry.data) {
             return;
         }
-        struct json_array *pa = json_array(r->entry.data);
+        const struct json *pa = r->entry.data;
 
-        if (pa->n != 2) {
+        if (json_array_size(pa) != 2) {
             ovs_fatal(0, "Incorrect raft record array length");
         }
 
-        struct json *schema_json = pa->elems[0];
-        struct json *data_json = pa->elems[1];
+        const struct json *schema_json = json_array_at(pa, 0);
+        const struct json *data_json = json_array_at(pa, 1);
+        struct json *new_data = NULL;
 
         if (schema_json->type != JSON_NULL) {
             /* This is a database conversion record.  Reset the log and
@@ -1041,12 +1042,10 @@  raft_record_to_standalone_log(const char *db_file_name,
                 check_ovsdb_error(ovsdb_convert(old_db, schema, &new_db));
                 ovsdb_destroy(old_db);
 
-                pa->elems[1] = ovsdb_to_txn_json(
+                new_data = ovsdb_to_txn_json(
                                     new_db, "converted by ovsdb-tool", true);
                 ovsdb_destroy(new_db);
-
-                json_destroy(data_json);
-                data_json = pa->elems[1];
+                data_json = new_data;
             }
 
             ovsdb_schema_destroy(schema);
@@ -1056,6 +1055,7 @@  raft_record_to_standalone_log(const char *db_file_name,
         if (data_json->type != JSON_NULL) {
             check_ovsdb_error(ovsdb_log_write(db_log_data, data_json));
         }
+        json_destroy(new_data);
     }
 }
 
diff --git a/ovsdb/raft-private.c b/ovsdb/raft-private.c
index 16ae6d572..99d505212 100644
--- a/ovsdb/raft-private.c
+++ b/ovsdb/raft-private.c
@@ -91,13 +91,14 @@  raft_addresses_from_json(const struct json *json, struct sset *addresses)
 {
     sset_init(addresses);
 
-    const struct json_array *array = json_array(json);
-    if (!array->n) {
+    size_t n = json_array_size(json);
+
+    if (!n) {
         return ovsdb_syntax_error(json, NULL,
                                   "at least one remote address is required");
     }
-    for (size_t i = 0; i < array->n; i++) {
-        const struct json *address = array->elems[i];
+    for (size_t i = 0; i < n; i++) {
+        const struct json *address = json_array_at(json, i);
         struct ovsdb_error *error = raft_address_validate_json(address);
         if (error) {
             sset_destroy(addresses);
@@ -325,7 +326,7 @@  raft_entry_to_json(const struct raft_entry *e)
 }
 
 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
-raft_entry_from_json(struct json *json, struct raft_entry *e)
+raft_entry_from_json(const struct json *json, struct raft_entry *e)
 {
     memset(e, 0, sizeof *e);
 
diff --git a/ovsdb/raft-private.h b/ovsdb/raft-private.h
index 48c6df511..8bfe85008 100644
--- a/ovsdb/raft-private.h
+++ b/ovsdb/raft-private.h
@@ -130,7 +130,8 @@  struct raft_entry {
 void raft_entry_clone(struct raft_entry *, const struct raft_entry *);
 void raft_entry_uninit(struct raft_entry *);
 struct json *raft_entry_to_json(const struct raft_entry *);
-struct ovsdb_error *raft_entry_from_json(struct json *, struct raft_entry *)
+struct ovsdb_error *raft_entry_from_json(const struct json *,
+                                         struct raft_entry *)
     OVS_WARN_UNUSED_RESULT;
 bool raft_entry_equals(const struct raft_entry *, const struct raft_entry *);
 bool raft_entry_has_data(const struct raft_entry *);
diff --git a/ovsdb/raft-rpc.c b/ovsdb/raft-rpc.c
index 846727c0a..e898a8b55 100644
--- a/ovsdb/raft-rpc.c
+++ b/ovsdb/raft-rpc.c
@@ -153,11 +153,13 @@  raft_append_request_from_jsonrpc(struct ovsdb_parser *p,
     if (!log) {
         return;
     }
-    const struct json_array *entries = json_array(log);
-    rq->entries = xmalloc(entries->n * sizeof *rq->entries);
+
+    size_t n = json_array_size(log);
+
+    rq->entries = xmalloc(n * sizeof *rq->entries);
     rq->n_entries = 0;
-    for (size_t i = 0; i < entries->n; i++) {
-        struct ovsdb_error *error = raft_entry_from_json(entries->elems[i],
+    for (size_t i = 0; i < n; i++) {
+        struct ovsdb_error *error = raft_entry_from_json(json_array_at(log, i),
                                                          &rq->entries[i]);
         if (error) {
             ovsdb_parser_put_error(p, error);
@@ -878,14 +880,14 @@  raft_rpc_from_jsonrpc(struct uuid *cidp, const struct uuid *sid,
         return ovsdb_error(NULL, "unknown method %s", msg->method);
     }
 
-    if (json_array(msg->params)->n != 1) {
+    if (json_array_size(msg->params) != 1) {
         return ovsdb_error(NULL,
                            "%s RPC has %"PRIuSIZE" parameters (expected 1)",
-                           msg->method, json_array(msg->params)->n);
+                           msg->method, json_array_size(msg->params));
     }
 
     struct ovsdb_parser p;
-    ovsdb_parser_init(&p, json_array(msg->params)->elems[0],
+    ovsdb_parser_init(&p, json_array_at(msg->params, 0),
                       "raft %s RPC", msg->method);
 
     bool is_hello = rpc->type == RAFT_RPC_HELLO_REQUEST;
diff --git a/ovsdb/replication.c b/ovsdb/replication.c
index 67eab9c42..bb21ff7d3 100644
--- a/ovsdb/replication.c
+++ b/ovsdb/replication.c
@@ -40,11 +40,11 @@  VLOG_DEFINE_THIS_MODULE(replication);
 
 static struct uuid server_uuid;
 
-static struct ovsdb_error *process_notification(struct json *, struct ovsdb *);
-static struct ovsdb_error *process_table_update(struct json *table_update,
-                                                const char *table_name,
-                                                struct ovsdb *database,
-                                                struct ovsdb_txn *txn);
+static struct ovsdb_error *process_notification(const struct json *,
+                                                struct ovsdb *);
+static struct ovsdb_error *process_table_update(
+    const struct json *table_update, const char *table_name,
+    struct ovsdb *database, struct ovsdb_txn *txn);
 
 enum ovsdb_replication_state {
     RPL_S_INIT,
@@ -218,13 +218,14 @@  replication_run_db(struct replication_db *rdb)
         if (msg->type == JSONRPC_NOTIFY && rdb->state != RPL_S_ERR
             && !strcmp(msg->method, "update")) {
             if (msg->params->type == JSON_ARRAY
-                && msg->params->array.n == 2
-                && msg->params->array.elems[0]->type == JSON_STRING) {
-                const char *db_name = json_string(msg->params->array.elems[0]);
+                && json_array_size(msg->params) == 2
+                && json_array_at(msg->params, 0)->type == JSON_STRING) {
+                const char *db_name = json_string(
+                                        json_array_at(msg->params, 0));
 
                 if (!strcmp(db_name, rdb->db->name)) {
                     struct ovsdb_error *error;
-                    error = process_notification(msg->params->array.elems[1],
+                    error = process_notification(json_array_at(msg->params, 1),
                                                  rdb->db);
                     if (error) {
                         ovsdb_error_assert(error);
@@ -592,7 +593,7 @@  add_monitored_table(struct ovsdb_table_schema *table,
 
 
 static struct ovsdb_error *
-process_notification(struct json *table_updates, struct ovsdb *db)
+process_notification(const struct json *table_updates, struct ovsdb *db)
 {
     struct ovsdb_error *error = NULL;
     struct ovsdb_txn *txn;
@@ -625,7 +626,7 @@  process_notification(struct json *table_updates, struct ovsdb *db)
 }
 
 static struct ovsdb_error *
-process_table_update(struct json *table_update, const char *table_name,
+process_table_update(const struct json *table_update, const char *table_name,
                      struct ovsdb *database, struct ovsdb_txn *txn)
 {
     struct ovsdb_table *table = ovsdb_get_table(database, table_name);
diff --git a/ovsdb/storage.c b/ovsdb/storage.c
index c5aec5459..a736bffb6 100644
--- a/ovsdb/storage.c
+++ b/ovsdb/storage.c
@@ -264,21 +264,24 @@  ovsdb_storage_read(struct ovsdb_storage *storage,
         *txnid = UUID_ZERO;
     }
 
+    const struct json *schema_json = NULL;
+    const struct json *txn_json = NULL;
     struct json *json;
-    struct json *schema_json = NULL;
-    struct json *txn_json = NULL;
+
     if (storage->raft) {
         json = raft_next_entry(storage->raft, txnid);
         if (!json) {
             return NULL;
-        } else if (json->type != JSON_ARRAY || json->array.n != 2) {
+        } else if (json->type != JSON_ARRAY || json_array_size(json) != 2) {
             json_destroy(json);
             return ovsdb_error(NULL, "invalid commit format");
         }
 
-        struct json **e = json->array.elems;
-        schema_json = e[0]->type != JSON_NULL ? e[0] : NULL;
-        txn_json = e[1]->type != JSON_NULL ? e[1] : NULL;
+        const struct json *e0 = json_array_at(json, 0);
+        const struct json *e1 = json_array_at(json, 1);
+
+        schema_json = e0->type != JSON_NULL ? e0 : NULL;
+        txn_json = e1->type != JSON_NULL ? e1 : NULL;
     } else if (storage->log) {
         struct ovsdb_error *error = ovsdb_log_read(storage->log, &json);
         if (error || !json) {
@@ -286,7 +289,7 @@  ovsdb_storage_read(struct ovsdb_storage *storage,
         }
 
         unsigned int n = storage->n_read++;
-        struct json **jsonp = !n ? &schema_json : &txn_json;
+        const struct json **jsonp = !n ? &schema_json : &txn_json;
         *jsonp = json;
         if (n == 1) {
             ovsdb_log_mark_base(storage->log);
diff --git a/ovsdb/table.c b/ovsdb/table.c
index 3e89ddd44..589ee5e8c 100644
--- a/ovsdb/table.c
+++ b/ovsdb/table.c
@@ -186,14 +186,14 @@  ovsdb_table_schema_from_json(const struct json *json, const char *name,
     }
 
     if (indexes) {
-        size_t i;
+        size_t i, n = json_array_size(indexes);
 
-        ts->indexes = xmalloc(indexes->array.n * sizeof *ts->indexes);
-        for (i = 0; i < indexes->array.n; i++) {
+        ts->indexes = xmalloc(n * sizeof *ts->indexes);
+        for (i = 0; i < n; i++) {
             struct ovsdb_column_set *index = &ts->indexes[i];
             size_t j;
 
-            error = ovsdb_column_set_from_json(indexes->array.elems[i],
+            error = ovsdb_column_set_from_json(json_array_at(indexes, i),
                                                ts, index);
             if (error) {
                 goto error;
diff --git a/ovsdb/trigger.c b/ovsdb/trigger.c
index 8c00fec18..7e3b9a197 100644
--- a/ovsdb/trigger.c
+++ b/ovsdb/trigger.c
@@ -291,14 +291,14 @@  ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now)
 
             /* Validate parameters. */
             const struct json *params = t->request->params;
-            if (params->type != JSON_ARRAY || params->array.n != 2) {
+            if (params->type != JSON_ARRAY || json_array_size(params) != 2) {
                 trigger_convert_error(t, ovsdb_syntax_error(params, NULL,
                                                             "array expected"));
                 return false;
             }
 
             /* Parse new schema and make a converted copy. */
-            const struct json *new_schema_json = params->array.elems[1];
+            const struct json *new_schema_json = json_array_at(params, 1);
             struct ovsdb_schema *new_schema;
             struct ovsdb_error *error
                 = ovsdb_schema_from_json(new_schema_json, &new_schema);
diff --git a/python/ovs/_json.c b/python/ovs/_json.c
index 5f4388f80..bd9f5fe19 100644
--- a/python/ovs/_json.c
+++ b/python/ovs/_json.c
@@ -111,14 +111,14 @@  json_to_python(struct json *json)
             return dict;
         }
     case JSON_ARRAY:{
-            size_t i;
-            PyObject *arr = PyList_New(json->array.n);
+            size_t i, n = json_array_size(json);
+            PyObject *arr = PyList_New(n);
 
             if (arr == NULL) {
                 return PyErr_NoMemory();
             }
-            for (i = 0; i < json->array.n; i++) {
-                PyObject *item = json_to_python(json->array.elems[i]);
+            for (i = 0; i < n; i++) {
+                PyObject *item = json_to_python(json_array_at(json, i));
 
                 if (!item || PyList_SetItem(arr, i, item)) {
                     Py_XDECREF(arr);
diff --git a/tests/test-json.c b/tests/test-json.c
index e7992e510..f5b0ad371 100644
--- a/tests/test-json.c
+++ b/tests/test-json.c
@@ -60,7 +60,7 @@  test_json_equal_object(const struct shash *a, const struct shash *b,
 }
 
 static void
-test_json_equal_array(const struct json_array *a, const struct json_array *b,
+test_json_equal_array(const struct json *a, const struct json *b,
                       bool allow_the_same)
 {
     ovs_assert(allow_the_same || a != b);
@@ -69,10 +69,12 @@  test_json_equal_array(const struct json_array *a, const struct json_array *b,
         return;
     }
 
-    ovs_assert(a->n == b->n);
+    size_t n = json_array_size(a);
+    ovs_assert(n == json_array_size(b));
 
-    for (size_t i = 0; i < a->n; i++) {
-        test_json_equal(a->elems[i], b->elems[i], allow_the_same);
+    for (size_t i = 0; i < n; i++) {
+        test_json_equal(json_array_at(a, i), json_array_at(b, i),
+                        allow_the_same);
     }
 }
 
@@ -96,7 +98,7 @@  test_json_equal(const struct json *a, const struct json *b,
         return;
 
     case JSON_ARRAY:
-        test_json_equal_array(&a->array, &b->array, allow_the_same);
+        test_json_equal_array(a, b, allow_the_same);
         return;
 
     case JSON_STRING:
diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
index ad4ca3b42..3d54d4ca2 100644
--- a/tests/test-ovsdb.c
+++ b/tests/test-ovsdb.c
@@ -273,9 +273,8 @@  parse_json(const char *s)
 static struct json *
 unbox_json(struct json *json)
 {
-    if (json->type == JSON_ARRAY && json->array.n == 1) {
-        struct json *inner = json->array.elems[0];
-        json->array.elems[0] = NULL;
+    if (json->type == JSON_ARRAY && json_array_size(json) == 1) {
+        struct json *inner = json_array_pop(json);
         json_destroy(json);
         return inner;
     } else {
@@ -788,11 +787,11 @@  do_sort_atoms(struct ovs_cmdl_context *ctx)
     }
 
     /* Convert JSON atoms to internal representation. */
-    n_atoms = json->array.n;
+    n_atoms = json_array_size(json);
     atoms = xmalloc(n_atoms * sizeof *atoms);
     for (i = 0; i < n_atoms; i++) {
         check_ovsdb_error(ovsdb_atom_from_json(&atoms[i], &base,
-                                               json->array.elems[i], NULL));
+                                               json_array_at(json, i), NULL));
     }
     json_destroy(json);
 
@@ -932,13 +931,13 @@  do_compare_rows(struct ovs_cmdl_context *ctx)
         rows[i] = ovsdb_row_create(table);
 
         json = parse_json(ctx->argv[i + 2]);
-        if (json->type != JSON_ARRAY || json->array.n != 2
-            || json->array.elems[0]->type != JSON_STRING) {
+        if (json->type != JSON_ARRAY || json_array_size(json) != 2
+            || json_array_at(json, 0)->type != JSON_STRING) {
             ovs_fatal(0, "\"%s\" does not have expected form "
                       "[\"name\", {data}]", ctx->argv[i]);
         }
-        names[i] = xstrdup(json_string(json->array.elems[0]));
-        check_ovsdb_error(ovsdb_row_from_json(rows[i], json->array.elems[1],
+        names[i] = xstrdup(json_string(json_array_at(json, 0)));
+        check_ovsdb_error(ovsdb_row_from_json(rows[i], json_array_at(json, 1),
                                               NULL, NULL, false));
         json_destroy(json);
     }
@@ -1034,10 +1033,10 @@  do_evaluate_condition__(struct ovs_cmdl_context *ctx, int mode)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "CONDITION argument is not JSON array");
     }
-    n_conditions = json->array.n;
+    n_conditions = json_array_size(json);
     conditions = xmalloc(n_conditions * sizeof *conditions);
     for (i = 0; i < n_conditions; i++) {
-        check_ovsdb_error(ovsdb_condition_from_json(ts, json->array.elems[i],
+        check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i),
                                                     NULL, &conditions[i]));
     }
     json_destroy(json);
@@ -1047,11 +1046,11 @@  do_evaluate_condition__(struct ovs_cmdl_context *ctx, int mode)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "ROW argument is not JSON array");
     }
-    n_rows = json->array.n;
+    n_rows = json_array_size(json);
     rows = xmalloc(n_rows * sizeof *rows);
     for (i = 0; i < n_rows; i++) {
         rows[i] = ovsdb_row_create(table);
-        check_ovsdb_error(ovsdb_row_from_json(rows[i], json->array.elems[i],
+        check_ovsdb_error(ovsdb_row_from_json(rows[i], json_array_at(json, i),
                                               NULL, NULL, false));
     }
     json_destroy(json);
@@ -1123,11 +1122,11 @@  do_compare_conditions(struct ovs_cmdl_context *ctx)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "CONDITION argument is not JSON array");
     }
-    n_conditions = json->array.n;
+    n_conditions = json_array_size(json);
     conditions = xmalloc(n_conditions * sizeof *conditions);
 
     for (i = 0; i < n_conditions; i++) {
-        check_ovsdb_error(ovsdb_condition_from_json(ts, json->array.elems[i],
+        check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i),
                                                     NULL, &conditions[i]));
     }
     json_destroy(json);
@@ -1207,11 +1206,11 @@  do_execute_mutations(struct ovs_cmdl_context *ctx)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "MUTATION argument is not JSON array");
     }
-    n_sets = json->array.n;
+    n_sets = json_array_size(json);
     sets = xmalloc(n_sets * sizeof *sets);
     for (i = 0; i < n_sets; i++) {
         check_ovsdb_error(ovsdb_mutation_set_from_json(ts,
-                                                       json->array.elems[i],
+                                                       json_array_at(json, i),
                                                        NULL, &sets[i]));
     }
     json_destroy(json);
@@ -1221,11 +1220,11 @@  do_execute_mutations(struct ovs_cmdl_context *ctx)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "ROW argument is not JSON array");
     }
-    n_rows = json->array.n;
+    n_rows = json_array_size(json);
     rows = xmalloc(n_rows * sizeof *rows);
     for (i = 0; i < n_rows; i++) {
         rows[i] = ovsdb_row_create(table);
-        check_ovsdb_error(ovsdb_row_from_json(rows[i], json->array.elems[i],
+        check_ovsdb_error(ovsdb_row_from_json(rows[i], json_array_at(json, i),
                                               NULL, NULL, false));
     }
     json_destroy(json);
@@ -1333,13 +1332,13 @@  do_query(struct ovs_cmdl_context *ctx)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "ROW argument is not JSON array");
     }
-    cbdata.n_rows = json->array.n;
+    cbdata.n_rows = json_array_size(json);
     cbdata.row_uuids = xmalloc(cbdata.n_rows * sizeof *cbdata.row_uuids);
     cbdata.counts = xmalloc(cbdata.n_rows * sizeof *cbdata.counts);
     for (i = 0; i < cbdata.n_rows; i++) {
         struct ovsdb_row *row = ovsdb_row_create(table);
         uuid_generate(ovsdb_row_get_uuid_rw(row));
-        check_ovsdb_error(ovsdb_row_from_json(row, json->array.elems[i],
+        check_ovsdb_error(ovsdb_row_from_json(row, json_array_at(json, i),
                                               NULL, NULL, false));
         if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) {
             ovs_fatal(0, "duplicate UUID "UUID_FMT" in table",
@@ -1355,11 +1354,11 @@  do_query(struct ovs_cmdl_context *ctx)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "CONDITION argument is not JSON array");
     }
-    for (i = 0; i < json->array.n; i++) {
+    for (i = 0; i < json_array_size(json); i++) {
         struct ovsdb_condition cnd;
         size_t j;
 
-        check_ovsdb_error(ovsdb_condition_from_json(ts, json->array.elems[i],
+        check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i),
                                                     NULL, &cnd));
 
         memset(cbdata.counts, 0, cbdata.n_rows * sizeof *cbdata.counts);
@@ -1435,7 +1434,7 @@  do_query_distinct(struct ovs_cmdl_context *ctx)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "ROW argument is not JSON array");
     }
-    n_rows = json->array.n;
+    n_rows = json_array_size(json);
     rows = xmalloc(n_rows * sizeof *rows);
     classes = xmalloc(n_rows * sizeof *classes);
     n_classes = 0;
@@ -1446,7 +1445,7 @@  do_query_distinct(struct ovs_cmdl_context *ctx)
         /* Parse row. */
         row = ovsdb_row_create(table);
         uuid_generate(ovsdb_row_get_uuid_rw(row));
-        check_ovsdb_error(ovsdb_row_from_json(row, json->array.elems[i],
+        check_ovsdb_error(ovsdb_row_from_json(row, json_array_at(json, i),
                                               NULL, NULL, false));
 
         /* Initialize row and find equivalence class. */
@@ -1479,12 +1478,12 @@  do_query_distinct(struct ovs_cmdl_context *ctx)
     if (json->type != JSON_ARRAY) {
         ovs_fatal(0, "CONDITION argument is not JSON array");
     }
-    for (i = 0; i < json->array.n; i++) {
+    for (i = 0; i < json_array_size(json); i++) {
         struct ovsdb_row_set results;
         struct ovsdb_condition cnd;
         size_t j;
 
-        check_ovsdb_error(ovsdb_condition_from_json(ts, json->array.elems[i],
+        check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i),
                                                     NULL, &cnd));
 
         for (j = 0; j < n_classes; j++) {
@@ -1646,11 +1645,11 @@  do_trigger(struct ovs_cmdl_context *ctx)
     for (i = 2; i < ctx->argc; i++) {
         struct json *params = parse_json(ctx->argv[i]);
         if (params->type == JSON_ARRAY
-            && json_array(params)->n == 2
-            && json_array(params)->elems[0]->type == JSON_STRING
-            && !strcmp(json_string(json_array(params)->elems[0]), "advance")
-            && json_array(params)->elems[1]->type == JSON_INTEGER) {
-            now += json_integer(json_array(params)->elems[1]);
+            && json_array_size(params) == 2
+            && json_array_at(params, 0)->type == JSON_STRING
+            && !strcmp(json_string(json_array_at(params, 0)), "advance")
+            && json_array_at(params, 1)->type == JSON_INTEGER) {
+            now += json_integer(json_array_at(params, 1));
             json_destroy(params);
         } else {
             struct test_trigger *t = xmalloc(sizeof *t);
@@ -1885,10 +1884,10 @@  do_transact(struct ovs_cmdl_context *ctx)
                       "with at least 1 element", i);
         }
 
-        n_args = command->array.n;
+        n_args = json_array_size(command);
         args = xmalloc((n_args + 1) * sizeof *args);
         for (j = 0; j < n_args; j++) {
-            struct json *s = command->array.elems[j];
+            const struct json *s = json_array_at(command, j);
             if (s->type != JSON_STRING) {
                 ovs_fatal(0, "transaction %d argument %d must be JSON string",
                           i, j);
@@ -2395,8 +2394,8 @@  parse_uuids(const struct json *json, struct ovsdb_symbol_table *symtab,
     } else if (json->type == JSON_ARRAY) {
         size_t i;
 
-        for (i = 0; i < json->array.n; i++) {
-            parse_uuids(json->array.elems[i], symtab, n);
+        for (i = 0; i < json_array_size(json); i++) {
+            parse_uuids(json_array_at(json, i), symtab, n);
         }
     } else if (json->type == JSON_OBJECT) {
         const struct shash_node *node;
@@ -2422,10 +2421,11 @@  substitute_uuids(struct json *json, const struct ovsdb_symbol_table *symtab)
             json->str_ptr = xasprintf(UUID_FMT, UUID_ARGS(&symbol->uuid));
         }
     } else if (json->type == JSON_ARRAY) {
-        size_t i;
+        size_t i, n = json_array_size(json);
 
-        for (i = 0; i < json->array.n; i++) {
-            substitute_uuids(json->array.elems[i], symtab);
+        for (i = 0; i < n; i++) {
+            substitute_uuids(CONST_CAST(struct json *, json_array_at(json, i)),
+                             symtab);
         }
     } else if (json->type == JSON_OBJECT) {
         const struct shash_node *node;
@@ -2728,25 +2728,26 @@  update_conditions(struct ovsdb_idl *idl, char *commands, int step)
         }
 
         struct ovsdb_idl_condition cond = OVSDB_IDL_CONDITION_INIT(&cond);
-        for (i = 0; i < json->array.n; i++) {
-            const struct json *clause = json->array.elems[i];
+        for (i = 0; i < json_array_size(json); i++) {
+            const struct json *clause = json_array_at(json, i);
             if (clause->type == JSON_TRUE) {
                 ovsdb_idl_condition_add_clause_true(&cond);
-            } else if (clause->type != JSON_ARRAY || clause->array.n != 3
-                       || clause->array.elems[0]->type != JSON_STRING
-                       || clause->array.elems[1]->type != JSON_STRING) {
+            } else if (clause->type != JSON_ARRAY
+                       || json_array_size(clause) != 3
+                       || json_array_at(clause, 0)->type != JSON_STRING
+                       || json_array_at(clause, 1)->type != JSON_STRING) {
                 ovs_fatal(0, "Error parsing condition");
             } else {
                 enum ovsdb_function function;
-                const char *function_s = json_string(clause->array.elems[1]);
+                const char *function_s = json_string(json_array_at(clause, 1));
                 struct ovsdb_error *error = ovsdb_function_from_string(
                     function_s, &function);
                 if (error) {
                     ovs_fatal(0, "unknown clause function %s", function_s);
                 }
 
-                const char *column = json_string(clause->array.elems[0]);
-                const struct json *arg = clause->array.elems[2];
+                const char *column = json_string(json_array_at(clause, 0));
+                const struct json *arg = json_array_at(clause, 2);
                 if (!strcmp(table_name, "simple")) {
                     parse_simple_json_clause(&cond, function, column, arg);
                 } else if (!strcmp(table_name, "link1")) {