Add a minireflect based json parser.

This parser takes decent json and parses it into a flatbuffer.  The
standard library for flatbuffers needs the full fbs definitions for all
the flatbuffers to do this job.

And add a flatbuffer to JSON function.

Change-Id: Ibc6dcd3fcbd7ac9cf9121d8258d1613d8d20661c
diff --git a/aos/BUILD b/aos/BUILD
index 2bd5791..83b8a18 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -1,4 +1,5 @@
 load("//tools:environments.bzl", "mcu_cpus")
+load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
 load("//aos/build:queues.bzl", "queue_library")
 
 filegroup(
@@ -409,3 +410,30 @@
     ],
     visibility = ["//visibility:public"],
 )
+
+flatbuffer_cc_library(
+    name = "json_to_flatbuffer_flatbuffer",
+    srcs = ["json_to_flatbuffer.fbs"],
+)
+
+cc_library(
+    name = "json_to_flatbuffer",
+    srcs = ["json_to_flatbuffer.cc"],
+    hdrs = ["json_to_flatbuffer.h"],
+    deps = [
+        "//aos/logging",
+        "@com_github_google_flatbuffers//:flatbuffers",
+    ],
+)
+
+cc_test(
+    name = "json_to_flatbuffer_test",
+    srcs = [
+        "json_to_flatbuffer_test.cc",
+    ],
+    deps = [
+        ":json_to_flatbuffer",
+        ":json_to_flatbuffer_flatbuffer",
+        "//aos/testing:googletest",
+    ],
+)
diff --git a/aos/json_to_flatbuffer.cc b/aos/json_to_flatbuffer.cc
new file mode 100644
index 0000000..d73089b
--- /dev/null
+++ b/aos/json_to_flatbuffer.cc
@@ -0,0 +1,1112 @@
+#include "aos/json_to_flatbuffer.h"
+
+#include <cstddef>
+#include "stdio.h"
+
+#include "aos/logging/logging.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/minireflect.h"
+
+// TODO(austin): Can we just do an Offset<void> ?  It doesn't matter, so maybe
+// just say that.
+//
+// TODO(austin): I've yet to see how to create an ET_UTYPE, so I don't know what
+// one is and how to test it.  So everything rejects it.
+
+namespace aos {
+
+// Finds the field index in the table given the name.
+int FieldIndex(const flatbuffers::TypeTable *typetable,
+               const char *field_name) {
+  CHECK(typetable->values == nullptr);
+  for (size_t i = 0; i < typetable->num_elems; ++i) {
+    if (strcmp(field_name, typetable->names[i]) == 0) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+namespace {
+
+// Class to hold one of the 3 json types for an array.
+struct Element {
+  // The type.
+  enum class ElementType { INT, DOUBLE, OFFSET };
+
+  // Constructs an Element holding an integer.
+  Element(int64_t new_int_element)
+      : int_element(new_int_element), type(ElementType::INT) {}
+  // Constructs an Element holding an double.
+  Element(double new_double_element)
+      : double_element(new_double_element), type(ElementType::DOUBLE) {}
+  // Constructs an Element holding an Offset.
+  Element(flatbuffers::Offset<flatbuffers::String> new_offset_element)
+      : offset_element(new_offset_element), type(ElementType::OFFSET) {}
+
+  // Union for the various datatypes.
+  union {
+    int64_t int_element;
+    double double_element;
+    flatbuffers::Offset<flatbuffers::String> offset_element;
+  };
+
+  // And an enum signaling which one is in use.
+  ElementType type;
+};
+
+// Structure to represent a field element.
+struct FieldElement {
+  FieldElement(int new_field_index, int64_t int_element)
+      : element(int_element), field_index(new_field_index) {}
+  FieldElement(int new_field_index, double double_element)
+      : element(double_element), field_index(new_field_index) {}
+  FieldElement(int new_field_index,
+               flatbuffers::Offset<flatbuffers::String> offset_element)
+      : element(offset_element), field_index(new_field_index) {}
+
+  // Data to write.
+  Element element;
+  // Field index.  The type table which this index is for is stored outside this
+  // object.
+  int field_index;
+};
+
+// Class to parse JSON into a flatbuffer.
+//
+// The basic strategy is that we need to do everything backwards.  So we need to
+// build up what we need to do fully in memory, then do it.
+//
+// The driver for this is that strings need to be fully created before the
+// tables that use them.  Same for sub messages.  But, we only know we have them
+// all when the structure ends.  So, store each sub message in a FieldElement
+// and put them in the table at the end when we finish up each message.  Same
+// goes for vectors.
+class JsonParser {
+ public:
+  JsonParser() { fbb_.ForceDefaults(1); }
+  ~JsonParser() {}
+
+  // Parses the json into a flatbuffer.  Returns either an empty vector on
+  // error, or a vector with the flatbuffer data in it.
+  ::std::vector<uint8_t> Parse(const char *data,
+                               const flatbuffers::TypeTable *typetable) {
+    flatbuffers::uoffset_t end;
+    bool result = DoParse(typetable, data, &end);
+
+    if (result) {
+      // On success, finish the table and build the vector.
+      auto o = flatbuffers::Offset<flatbuffers::Table>(end);
+      fbb_.Finish(o);
+
+      const uint8_t *buf = fbb_.GetBufferPointer();
+      const int size = fbb_.GetSize();
+      return ::std::vector<uint8_t>(buf, buf + size);
+    } else {
+      // Otherwise return an empty vector.
+      return ::std::vector<uint8_t>();
+    }
+  }
+
+ private:
+  // Setters and getters for in_vector (at the current level of the stack)
+  bool in_vector() const { return stack_.back().in_vector; }
+  void set_in_vector(bool in_vector) { stack_.back().in_vector = in_vector; }
+
+  // Parses the flatbuffer.  This is a second method so we can do easier
+  // cleanup at the top level.  Returns true on success.
+  bool DoParse(const flatbuffers::TypeTable *typetable, const char *data,
+               flatbuffers::uoffset_t *table_end);
+
+  // Adds *_value for the provided field.  If we are in a vector, queues the
+  // data up in vector_elements_.  Returns true on success.
+  bool AddElement(int field_index, int64_t int_value);
+  bool AddElement(int field_index, double double_value);
+  bool AddElement(int field_index, const ::std::string &data);
+
+  // Adds a single element.  This assumes that vectors have been dealt with
+  // already.  Returns true on success.
+  bool AddSingleElement(const FieldElement &field_element,
+                        ::std::vector<bool> *fields_in_use);
+  bool AddSingleElement(int field_index, int64_t int_value);
+  bool AddSingleElement(int field_index, double double_value);
+  bool AddSingleElement(
+      int field_index, flatbuffers::Offset<flatbuffers::String> offset_element);
+
+  const char *ElementaryTypeName(
+      const flatbuffers::ElementaryType elementary_type) {
+    return flatbuffers::ElementaryTypeNames()[elementary_type] + 3;
+  }
+
+  // Finishes a vector for the provided field index.  Returns true on success.
+  bool FinishVector(int field_index);
+
+  // Pushes an element as part of a vector.  Returns true on success.
+  bool PushElement(flatbuffers::ElementaryType elementary_type,
+                   int64_t int_value);
+  bool PushElement(flatbuffers::ElementaryType elementary_type,
+                   double double_value);
+  bool PushElement(flatbuffers::ElementaryType elementary_type,
+                   flatbuffers::Offset<flatbuffers::String> offset_value);
+
+  flatbuffers::FlatBufferBuilder fbb_;
+
+  // This holds the state information that is needed as you recurse into
+  // nested structures.
+  struct FlatBufferContext {
+    // Type of the current type.
+    const flatbuffers::TypeTable *typetable;
+    // If true, we are parsing a vector.
+    bool in_vector;
+    // The field index of the current field.
+    int field_index;
+    // Name of the current field.
+    ::std::string field_name;
+
+    // Field elements that need to be inserted.
+    ::std::vector<FieldElement> elements;
+  };
+  ::std::vector<FlatBufferContext> stack_;
+
+  // For scalar types (not strings, and not nested tables), the vector ends
+  // up being implemented as a start and end, and a block of data.  So we
+  // can't just push offsets in as we go.  We either need to reproduce the
+  // logic inside flatbuffers, or build up vectors of the data.  Vectors will
+  // be a bit of extra stack space, but whatever.
+  //
+  // Strings and nested structures are vectors of offsets.
+  // into the vector. Once you get to the end, you build up a vector and
+  // push that into the field.
+  ::std::vector<Element> vector_elements_;
+};
+
+bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable,
+                         const char *data, flatbuffers::uoffset_t *table_end) {
+  ::std::vector<const flatbuffers::TypeTable *> stack;
+
+  Tokenizer t(data);
+
+  // Main loop.  Run until we get an end.
+  while (true) {
+    Tokenizer::TokenType token = t.Next();
+
+    switch (token) {
+      case Tokenizer::TokenType::kEnd:
+        if (stack_.size() != 0) {
+          printf("Failed to unwind stack all the way\n");
+          return false;
+        } else {
+          return true;
+        }
+        break;
+      case Tokenizer::TokenType::kError:
+        return false;
+        break;
+
+      case Tokenizer::TokenType::kStartObject:  // {
+        if (stack_.size() == 0) {
+          stack_.push_back({typetable, false, -1, "", {}});
+        } else {
+          int field_index = stack_.back().field_index;
+
+          const flatbuffers::TypeCode &type_code =
+              stack_.back().typetable->type_codes[field_index];
+
+          if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
+            printf("Field '%s' is not a sequence\n",
+                   stack_.back().field_name.c_str());
+            return false;
+          }
+
+          flatbuffers::TypeFunction type_function =
+              stack_.back().typetable->type_refs[type_code.sequence_ref];
+
+          stack_.push_back({type_function(), false, -1, "", {}});
+        }
+        break;
+      case Tokenizer::TokenType::kEndObject:  // }
+        if (stack_.size() == 0) {
+          // Somehow we popped more than we pushed.  Error.
+          printf("Empty stack\n");
+          return false;
+        } else {
+          // End of a nested struct!  Add it.
+          const flatbuffers::uoffset_t start = fbb_.StartTable();
+
+          ::std::vector<bool> fields_in_use(stack_.back().typetable->num_elems,
+                                            false);
+
+          for (const FieldElement &field_element : stack_.back().elements) {
+            AddSingleElement(field_element, &fields_in_use);
+          }
+
+          const flatbuffers::uoffset_t end = fbb_.EndTable(start);
+
+          // We now want to talk about the parent structure.  Pop the child.
+          stack_.pop_back();
+
+          if (stack_.size() == 0) {
+            // Instead of queueing it up in the stack, return it through the
+            // passed in variable.
+            *table_end = end;
+          } else {
+            // And now we can add it.
+            const int field_index = stack_.back().field_index;
+
+            // Do the right thing if we are in a vector.
+            if (in_vector()) {
+              vector_elements_.emplace_back(
+                  flatbuffers::Offset<flatbuffers::String>(end));
+            } else {
+              stack_.back().elements.emplace_back(
+                  field_index, flatbuffers::Offset<flatbuffers::String>(end));
+            }
+          }
+        }
+        break;
+
+      case Tokenizer::TokenType::kStartArray:  // [
+        if (stack_.size() == 0) {
+          // We don't support an array of structs at the root level.
+          return false;
+        }
+        // Sanity check that we aren't trying to make a vector of vectors.
+        if (in_vector()) {
+          return false;
+        }
+        set_in_vector(true);
+
+        break;
+      case Tokenizer::TokenType::kEndArray: {  // ]
+        if (!in_vector()) {
+          return false;
+        }
+
+        const int field_index = stack_.back().field_index;
+
+        if (!FinishVector(field_index)) return false;
+
+        set_in_vector(false);
+      } break;
+
+      case Tokenizer::TokenType::kTrueValue:   // true
+      case Tokenizer::TokenType::kFalseValue:  // false
+      case Tokenizer::TokenType::kNumberValue: {
+        bool is_int = true;
+        double double_value;
+        long long int_value;
+        if (token == Tokenizer::TokenType::kTrueValue) {
+          int_value = 1;
+        } else if (token == Tokenizer::TokenType::kFalseValue) {
+          int_value = 0;
+        } else if (!t.FieldAsInt(&int_value)) {
+          if (t.FieldAsDouble(&double_value)) {
+            is_int = false;
+          } else {
+            fprintf(stderr, "Got a invalid number '%s'\n",
+                    t.field_value().c_str());
+            return false;
+          }
+        }
+
+        const int field_index = stack_.back().field_index;
+
+        if (is_int) {
+          // No need to get too stressed about bool vs int.  Convert them all.
+          int64_t val = int_value;
+          if (!AddElement(field_index, val)) return false;
+        } else {
+          if (!AddElement(field_index, double_value)) return false;
+        }
+      } break;
+      // TODO(austin): Need to detect int vs float.
+      /*
+      asdf
+      {
+        const int field_index = stack_.back().field_index;
+
+      } break;
+      */
+      case Tokenizer::TokenType::kStringValue:  // string value
+      {
+        const int field_index = stack_.back().field_index;
+
+        if (!AddElement(field_index, t.field_value())) return false;
+      } break;
+      case Tokenizer::TokenType::kField:  // field name
+      {
+        stack_.back().field_name = t.field_name();
+        stack_.back().field_index = FieldIndex(
+            stack_.back().typetable, stack_.back().field_name.c_str());
+
+        if (stack_.back().field_index == -1) {
+          printf("Invalid field name '%s'\n", stack_.back().field_name.c_str());
+          return false;
+        }
+      } break;
+    }
+  }
+  return false;
+}
+
+bool JsonParser::AddElement(int field_index, int64_t int_value) {
+  flatbuffers::TypeCode type_code =
+      stack_.back().typetable->type_codes[field_index];
+
+  if (type_code.is_vector != in_vector()) {
+    printf("Type and json disagree on if we are in a vector or not\n");
+    return false;
+  }
+
+  if (in_vector()) {
+    vector_elements_.emplace_back(int_value);
+  } else {
+    stack_.back().elements.emplace_back(field_index, int_value);
+  }
+  return true;
+}
+
+bool JsonParser::AddElement(int field_index, double double_value) {
+  flatbuffers::TypeCode type_code =
+      stack_.back().typetable->type_codes[field_index];
+
+  if (type_code.is_vector != in_vector()) {
+    printf("Type and json disagree on if we are in a vector or not\n");
+    return false;
+  }
+
+  if (in_vector()) {
+    vector_elements_.emplace_back(double_value);
+  } else {
+    stack_.back().elements.emplace_back(field_index, double_value);
+  }
+  return true;
+}
+
+bool JsonParser::AddElement(int field_index, const ::std::string &data) {
+  flatbuffers::TypeCode type_code =
+      stack_.back().typetable->type_codes[field_index];
+
+  if (type_code.is_vector != in_vector()) {
+    printf("Type and json disagree on if we are in a vector or not\n");
+    return false;
+  }
+
+  if (in_vector()) {
+    vector_elements_.emplace_back(fbb_.CreateString(data));
+
+  } else {
+    stack_.back().elements.emplace_back(field_index, fbb_.CreateString(data));
+  }
+  return true;
+}
+
+bool JsonParser::AddSingleElement(const FieldElement &field_element,
+                                  ::std::vector<bool> *fields_in_use) {
+  if ((*fields_in_use)[field_element.field_index]) {
+    printf("Duplicate field: '%s'\n",
+           stack_.back().typetable->names[field_element.field_index]);
+    return false;
+  }
+
+  (*fields_in_use)[field_element.field_index] = true;
+
+  switch (field_element.element.type) {
+    case Element::ElementType::INT:
+      return AddSingleElement(field_element.field_index,
+                              field_element.element.int_element);
+    case Element::ElementType::DOUBLE:
+      return AddSingleElement(field_element.field_index,
+                              field_element.element.double_element);
+    case Element::ElementType::OFFSET:
+      return AddSingleElement(field_element.field_index,
+                              field_element.element.offset_element);
+  }
+  return false;
+}
+
+bool JsonParser::AddSingleElement(int field_index, int64_t int_value) {
+  flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
+      static_cast<flatbuffers::voffset_t>(field_index));
+
+  flatbuffers::TypeCode type_code =
+      stack_.back().typetable->type_codes[field_index];
+
+  const flatbuffers::ElementaryType elementary_type =
+      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+  switch (elementary_type) {
+    case flatbuffers::ET_BOOL:
+      fbb_.AddElement<bool>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_CHAR:
+      fbb_.AddElement<int8_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_UCHAR:
+      fbb_.AddElement<uint8_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_SHORT:
+      fbb_.AddElement<int16_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_USHORT:
+      fbb_.AddElement<uint16_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_INT:
+      fbb_.AddElement<int32_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_UINT:
+      fbb_.AddElement<uint32_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_LONG:
+      fbb_.AddElement<int64_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_ULONG:
+      fbb_.AddElement<uint64_t>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_FLOAT:
+      fbb_.AddElement<float>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_DOUBLE:
+      fbb_.AddElement<double>(field_offset, int_value, 0);
+      return true;
+    case flatbuffers::ET_STRING:
+    case flatbuffers::ET_UTYPE:
+    case flatbuffers::ET_SEQUENCE:
+      printf("Mismatched type for field '%s'. Got: integer, expected %s\n",
+             stack_.back().field_name.c_str(),
+             ElementaryTypeName(elementary_type));
+      return false;
+  };
+  return false;
+}
+
+bool JsonParser::AddSingleElement(int field_index, double double_value) {
+  flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
+      static_cast<flatbuffers::voffset_t>(field_index));
+
+  flatbuffers::TypeCode type_code =
+      stack_.back().typetable->type_codes[field_index];
+
+  const flatbuffers::ElementaryType elementary_type =
+      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+  switch (elementary_type) {
+    case flatbuffers::ET_UTYPE:
+    case flatbuffers::ET_BOOL:
+    case flatbuffers::ET_CHAR:
+    case flatbuffers::ET_UCHAR:
+    case flatbuffers::ET_SHORT:
+    case flatbuffers::ET_USHORT:
+    case flatbuffers::ET_INT:
+    case flatbuffers::ET_UINT:
+    case flatbuffers::ET_LONG:
+    case flatbuffers::ET_ULONG:
+    case flatbuffers::ET_STRING:
+    case flatbuffers::ET_SEQUENCE:
+      printf("Mismatched type for field '%s'. Got: double, expected %s\n",
+             stack_.back().field_name.c_str(),
+             ElementaryTypeName(elementary_type));
+      return false;
+    case flatbuffers::ET_FLOAT:
+      fbb_.AddElement<float>(field_offset, double_value, 0);
+      return true;
+    case flatbuffers::ET_DOUBLE:
+      fbb_.AddElement<double>(field_offset, double_value, 0);
+      return true;
+  }
+  return false;
+}
+bool JsonParser::AddSingleElement(
+    int field_index, flatbuffers::Offset<flatbuffers::String> offset_element) {
+  flatbuffers::TypeCode type_code =
+      stack_.back().typetable->type_codes[field_index];
+
+  flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
+      static_cast<flatbuffers::voffset_t>(field_index));
+
+  // Vectors will always be Offset<>'s.
+  if (type_code.is_vector) {
+    fbb_.AddOffset(field_offset, offset_element);
+    return true;
+  }
+
+  const flatbuffers::ElementaryType elementary_type =
+      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+  switch (elementary_type) {
+    case flatbuffers::ET_UTYPE:
+    case flatbuffers::ET_BOOL:
+    case flatbuffers::ET_CHAR:
+    case flatbuffers::ET_UCHAR:
+    case flatbuffers::ET_SHORT:
+    case flatbuffers::ET_USHORT:
+    case flatbuffers::ET_INT:
+    case flatbuffers::ET_UINT:
+    case flatbuffers::ET_LONG:
+    case flatbuffers::ET_ULONG:
+    case flatbuffers::ET_FLOAT:
+    case flatbuffers::ET_DOUBLE:
+      printf("Mismatched type for field '%s'. Got: string, expected %s\n",
+             stack_.back().field_name.c_str(),
+             ElementaryTypeName(elementary_type));
+      return false;
+    case flatbuffers::ET_SEQUENCE:
+    case flatbuffers::ET_STRING:
+      fbb_.AddOffset(field_offset, offset_element);
+      return true;
+  }
+  return false;
+}
+
+bool JsonParser::FinishVector(int field_index) {
+  flatbuffers::TypeCode type_code =
+      stack_.back().typetable->type_codes[field_index];
+
+  const flatbuffers::ElementaryType elementary_type =
+      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+
+  // Vectors have a start (unfortunately which needs to know the size)
+  fbb_.StartVector(
+      vector_elements_.size(),
+      flatbuffers::InlineSize(elementary_type, stack_.back().typetable));
+
+  // Then the data (in reverse order for some reason...)
+  for (size_t i = vector_elements_.size(); i > 0;) {
+    const Element &element = vector_elements_[--i];
+    switch (element.type) {
+      case Element::ElementType::INT:
+        if (!PushElement(elementary_type, element.int_element)) return false;
+        break;
+      case Element::ElementType::DOUBLE:
+        if (!PushElement(elementary_type, element.double_element)) return false;
+        break;
+      case Element::ElementType::OFFSET:
+        if (!PushElement(elementary_type, element.offset_element)) return false;
+        break;
+    }
+  }
+
+  // Then an End which is placed into the buffer the same as any other offset.
+  stack_.back().elements.emplace_back(
+      field_index, flatbuffers::Offset<flatbuffers::String>(
+                       fbb_.EndVector(vector_elements_.size())));
+  return true;
+}
+
+bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
+                             int64_t int_value) {
+  switch (elementary_type) {
+    case flatbuffers::ET_BOOL:
+      fbb_.PushElement<bool>(int_value);
+      return true;
+    case flatbuffers::ET_CHAR:
+      fbb_.PushElement<int8_t>(int_value);
+      return true;
+    case flatbuffers::ET_UCHAR:
+      fbb_.PushElement<uint8_t>(int_value);
+      return true;
+    case flatbuffers::ET_SHORT:
+      fbb_.PushElement<int16_t>(int_value);
+      return true;
+    case flatbuffers::ET_USHORT:
+      fbb_.PushElement<uint16_t>(int_value);
+      return true;
+    case flatbuffers::ET_INT:
+      fbb_.PushElement<int32_t>(int_value);
+      return true;
+    case flatbuffers::ET_UINT:
+      fbb_.PushElement<uint32_t>(int_value);
+      return true;
+    case flatbuffers::ET_LONG:
+      fbb_.PushElement<int64_t>(int_value);
+      return true;
+    case flatbuffers::ET_ULONG:
+      fbb_.PushElement<uint64_t>(int_value);
+      return true;
+    case flatbuffers::ET_FLOAT:
+      fbb_.PushElement<float>(int_value);
+      return true;
+    case flatbuffers::ET_DOUBLE:
+      fbb_.PushElement<double>(int_value);
+      return true;
+    case flatbuffers::ET_STRING:
+    case flatbuffers::ET_UTYPE:
+    case flatbuffers::ET_SEQUENCE:
+      printf("Mismatched type for field '%s'. Got: integer, expected %s\n",
+             stack_.back().field_name.c_str(),
+             ElementaryTypeName(elementary_type));
+      return false;
+  };
+  return false;
+}
+
+bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
+                             double double_value) {
+  switch (elementary_type) {
+    case flatbuffers::ET_UTYPE:
+    case flatbuffers::ET_BOOL:
+    case flatbuffers::ET_CHAR:
+    case flatbuffers::ET_UCHAR:
+    case flatbuffers::ET_SHORT:
+    case flatbuffers::ET_USHORT:
+    case flatbuffers::ET_INT:
+    case flatbuffers::ET_UINT:
+    case flatbuffers::ET_LONG:
+    case flatbuffers::ET_ULONG:
+    case flatbuffers::ET_STRING:
+    case flatbuffers::ET_SEQUENCE:
+      printf("Mismatched type for field '%s'. Got: double, expected %s\n",
+             stack_.back().field_name.c_str(),
+             ElementaryTypeName(elementary_type));
+      return false;
+    case flatbuffers::ET_FLOAT:
+      fbb_.PushElement<float>(double_value);
+      return true;
+    case flatbuffers::ET_DOUBLE:
+      fbb_.PushElement<double>(double_value);
+      return true;
+  }
+  return false;
+}
+
+bool JsonParser::PushElement(
+    flatbuffers::ElementaryType elementary_type,
+    flatbuffers::Offset<flatbuffers::String> offset_value) {
+  switch (elementary_type) {
+    case flatbuffers::ET_UTYPE:
+    case flatbuffers::ET_BOOL:
+    case flatbuffers::ET_CHAR:
+    case flatbuffers::ET_UCHAR:
+    case flatbuffers::ET_SHORT:
+    case flatbuffers::ET_USHORT:
+    case flatbuffers::ET_INT:
+    case flatbuffers::ET_UINT:
+    case flatbuffers::ET_LONG:
+    case flatbuffers::ET_ULONG:
+    case flatbuffers::ET_FLOAT:
+    case flatbuffers::ET_DOUBLE:
+      printf("Mismatched type for field '%s'. Got: sequence, expected %s\n",
+             stack_.back().field_name.c_str(),
+             ElementaryTypeName(elementary_type));
+      return false;
+    case flatbuffers::ET_STRING:
+    case flatbuffers::ET_SEQUENCE:
+      fbb_.PushElement(offset_value);
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+::std::vector<uint8_t> JsonToFlatbuffer(
+    const char *data, const flatbuffers::TypeTable *typetable) {
+  JsonParser p;
+  return p.Parse(data, typetable);
+}
+
+::std::string FlatbufferToJson(const uint8_t *buffer,
+                               const ::flatbuffers::TypeTable *typetable,
+                               bool multi_line) {
+  ::flatbuffers::ToStringVisitor tostring_visitor(
+      multi_line ? "\n" : " ", true, multi_line ? " " : "", multi_line);
+  IterateFlatBuffer(buffer, typetable, &tostring_visitor);
+  return tostring_visitor.s;
+}
+
+void Tokenizer::ConsumeWhitespace() {
+  while (true) {
+    if (*data_ == '\0') {
+      return;
+    }
+    // Skip any whitespace.
+    if (*data_ == ' ' || *data_ == '\r' || *data_ == '\t') {
+      ++data_;
+    } else if (*data_ == '\n') {
+      ++data_;
+      ++linenumber_;
+    } else {
+      // There is no fail.  Once we are out of whitespace (including 0 of it),
+      // declare success.
+      return;
+    }
+  }
+}
+
+bool Tokenizer::Consume(const char *token) {
+  const char *original = data_;
+  while (true) {
+    // Finishing the token is success.
+    if (*token == '\0') {
+      return true;
+    }
+
+    // But finishing the data first is failure.
+    if (*data_ == '\0') {
+      data_ = original;
+      return false;
+    }
+
+    // Missmatch is failure.
+    if (*token != *data_) {
+      data_ = original;
+      return false;
+    }
+
+    ++data_;
+    ++token;
+  }
+}
+
+bool Tokenizer::ConsumeString(::std::string *s) {
+  // Under no conditions is it acceptible to run out of data while parsing a
+  // string.  Any '\0' checks should confirm that.
+  const char *original = data_;
+  if (*data_ == '\0') {
+    return false;
+  }
+
+  // Expect the leading "
+  if (*data_ != '"') {
+    return false;
+  }
+
+  ++data_;
+  const char *last_parsed_data = data_;
+  *s = ::std::string();
+
+  while (true) {
+    if (*data_ == '\0') {
+      data_ = original;
+      return false;
+    }
+
+    // If we get an end or an escape, do something special.
+    if (*data_ == '"' || *data_ == '\\') {
+      // Save what we found up until now, not including this character.
+      *s += ::std::string(last_parsed_data, data_);
+
+      // Update the pointer.
+      last_parsed_data = data_;
+
+      // " is the end, declare victory.
+      if (*data_ == '"') {
+        ++data_;
+        return true;
+      } else {
+        ++data_;
+        // Now consume valid escape characters and add their representation onto
+        // the output string.
+        if (*data_ == '\0') {
+          data_ = original;
+          return false;
+        } else if (*data_ == '"') {
+          *s += "\"";
+        } else if (*data_ == '\\') {
+          *s += "\\";
+        } else if (*data_ == '/') {
+          *s += "/";
+        } else if (*data_ == 'b') {
+          *s += "\b";
+        } else if (*data_ == 'f') {
+          *s += "\f";
+        } else if (*data_ == 'n') {
+          *s += "\n";
+        } else if (*data_ == 'r') {
+          *s += "\r";
+        } else if (*data_ == 't') {
+          *s += "\t";
+        } else if (*data_ == 'u') {
+          // TODO(austin): Unicode should be valid, but I really don't care to
+          // do this now...
+          fprintf(stderr, "Unexpected unicode on line %d\n", linenumber_);
+          data_ = original;
+          return false;
+        }
+      }
+      // And skip the escaped character.
+      last_parsed_data = data_ + 1;
+    }
+
+    ++data_;
+  }
+}
+
+bool Tokenizer::ConsumeNumber(::std::string *s) {
+  // Under no conditions is it acceptible to run out of data while parsing a
+  // number.  Any '\0' checks should confirm that.
+  *s = ::std::string();
+  const char *original = data_;
+
+  // Consume the leading - unconditionally.
+  Consume("-");
+
+  // Then, we either get a 0, or we get a nonzero.  Only nonzero can be followed
+  // by a second number.
+  if (!Consume("0")) {
+    if (*data_ == '\0') {
+      return false;
+    } else if (*data_ >= '1' && *data_ <= '9') {
+      // This wasn't a zero, but was a valid digit.  Consume it.
+      ++data_;
+    } else {
+      return false;
+    }
+
+    // Now consume any number of any digits.
+    while (true) {
+      if (*data_ == '\0') {
+        data_ = original;
+        return false;
+      }
+      if (*data_ < '0' || *data_ > '9') {
+        break;
+      }
+      ++data_;
+    }
+  }
+
+  // We could now have a decimal.
+  if (*data_ == '.') {
+    ++data_;
+    while (true) {
+      if (*data_ == '\0') {
+        data_ = original;
+        return false;
+      }
+      // And any number of digits.
+      if (*data_ < '0' || *data_ > '9') {
+        break;
+      }
+      ++data_;
+    }
+  }
+
+  // And now an exponent.
+  if (*data_ == 'e' || *data_ == 'E') {
+    ++data_;
+    if (*data_ == '\0') {
+      data_ = original;
+      return false;
+    }
+
+    // Which could have a +-
+    if (*data_ == '+' || *data_ == '-') {
+      ++data_;
+    }
+    int count = 0;
+    while (true) {
+      if (*data_ == '\0') {
+        data_ = original;
+        return false;
+      }
+      // And digits.
+      if (*data_ < '0' || *data_ > '9') {
+        break;
+      }
+      ++data_;
+      ++count;
+    }
+    // But, it is an error to have an exponent and nothing following it.
+    if (count == 0) {
+      data_ = original;
+      return false;
+    }
+  }
+
+  *s = ::std::string(original, data_);
+  return true;
+}
+
+Tokenizer::TokenType Tokenizer::Next() {
+  switch (state_) {
+    case State::kExpectObjectStart:
+      // We should always start out with a {
+      if (!Consume("{")) return TokenType::kError;
+
+      // Document that we just started an object.
+      object_type_.push_back(ObjectType::kObject);
+
+      ConsumeWhitespace();
+
+      state_ = State::kExpectField;
+      return TokenType::kStartObject;
+
+    case State::kExpectField: {
+      // Fields are built up of strings, whitespace, and then a : (followed by
+      // whitespace...)
+      ::std::string s;
+      if (!ConsumeString(&s)) {
+        fprintf(stderr, "Error on line %d, expected string for field name.\n",
+                linenumber_);
+        return TokenType::kError;
+      }
+      field_name_ = ::std::move(s);
+
+      ConsumeWhitespace();
+
+      if (!Consume(":")) {
+        fprintf(stderr, "Error on line %d\n", linenumber_);
+        return TokenType::kError;
+      }
+
+      ConsumeWhitespace();
+
+      state_ = State::kExpectValue;
+
+      return TokenType::kField;
+    } break;
+    case State::kExpectValue: {
+      TokenType result = TokenType::kError;
+
+      ::std::string s;
+      if (Consume("{")) {
+        // Fields are in objects.  Record and recurse.
+        object_type_.push_back(ObjectType::kObject);
+
+        ConsumeWhitespace();
+
+        state_ = State::kExpectField;
+        return TokenType::kStartObject;
+      } else if (Consume("[")) {
+        // Values are in arrays.  Record and recurse.
+        object_type_.push_back(ObjectType::kArray);
+
+        ConsumeWhitespace();
+        state_ = State::kExpectValue;
+        return TokenType::kStartArray;
+      } else if (ConsumeString(&s)) {
+        // Parsed as a string, grab it.
+        field_value_ = ::std::move(s);
+        result = TokenType::kStringValue;
+      } else if (ConsumeNumber(&s)) {
+        // Parsed as a number, grab it.
+        field_value_ = ::std::move(s);
+        result = TokenType::kNumberValue;
+      } else if (Consume("true")) {
+        // Parsed as a true, grab it.
+        field_value_ = "true";
+        result = TokenType::kTrueValue;
+      } else if (Consume("false")) {
+        // Parsed as a false, grab it.
+        field_value_ = "false";
+        result = TokenType::kFalseValue;
+      } else {
+        // Couldn't parse, so we have a syntax error.
+        fprintf(stderr, "Error line %d, invalid field value.\n", linenumber_);
+      }
+
+      ConsumeWhitespace();
+
+      // After a field, we either have a , and another field (or value if we are
+      // in an array), or we should be closing out the object (or array).
+      if (Consume(",")) {
+        ConsumeWhitespace();
+        switch (object_type_.back()) {
+          case ObjectType::kObject:
+            state_ = State::kExpectField;
+            break;
+          case ObjectType::kArray:
+            state_ = State::kExpectValue;
+            break;
+        }
+      } else {
+        // Sanity check that the stack is deep enough.
+        if (object_type_.size() == 0) {
+          fprintf(stderr, "Error on line %d\n", linenumber_);
+          return TokenType::kError;
+        }
+
+        // And then require closing out the object.
+        switch (object_type_.back()) {
+          case ObjectType::kObject:
+            if (Consume("}")) {
+              ConsumeWhitespace();
+              state_ = State::kExpectObjectEnd;
+            } else {
+              return TokenType::kError;
+            }
+            break;
+          case ObjectType::kArray:
+            if (Consume("]")) {
+              ConsumeWhitespace();
+              state_ = State::kExpectArrayEnd;
+            } else {
+              return TokenType::kError;
+            }
+            break;
+        }
+      }
+      return result;
+    } break;
+
+    case State::kExpectArrayEnd:
+    case State::kExpectObjectEnd: {
+      const TokenType result = state_ == State::kExpectArrayEnd
+                                   ? TokenType::kEndArray
+                                   : TokenType::kEndObject;
+      // This is a transient state so we can send 2 tokens out in a row.  We
+      // discover the object or array end at the end of reading the value.
+      object_type_.pop_back();
+      if (object_type_.size() == 0) {
+        // We unwound the outer object.  We should send kEnd next.
+        state_ = State::kExpectEnd;
+      } else if (object_type_.back() == ObjectType::kObject) {
+        // If we are going into an object, it should either have another field
+        // or end.
+        if (Consume(",")) {
+          ConsumeWhitespace();
+          state_ = State::kExpectField;
+        } else if (Consume("}")) {
+          ConsumeWhitespace();
+          state_ = State::kExpectObjectEnd;
+        } else {
+          return TokenType::kError;
+        }
+      } else if (object_type_.back() == ObjectType::kArray) {
+        // If we are going into an array, it should either have another value
+        // or end.
+        if (Consume(",")) {
+          ConsumeWhitespace();
+          state_ = State::kExpectValue;
+        } else if (Consume("]")) {
+          ConsumeWhitespace();
+          state_ = State::kExpectArrayEnd;
+        } else {
+          return TokenType::kError;
+        }
+      }
+      // And then send out the correct token.
+      return result;
+    }
+    case State::kExpectEnd:
+      // If we are supposed to be done, confirm nothing is after the end.
+      if (AtEnd()) {
+        return TokenType::kEnd;
+      } else {
+        fprintf(stderr, "Data past end at line %d\n", linenumber_);
+        return TokenType::kError;
+      }
+  }
+  return TokenType::kError;
+}
+
+bool Tokenizer::FieldAsInt(long long *value) {
+  const char *pos = field_value().c_str();
+  errno = 0;
+  *value = strtoll(field_value().c_str(), const_cast<char **>(&pos), 10);
+  if (pos == field_value().c_str() || errno != 0) {
+    return false;
+  }
+  return true;
+}
+
+bool Tokenizer::FieldAsDouble(double *value) {
+  const char *pos = field_value().c_str();
+  errno = 0;
+  *value = strtod(field_value().c_str(), const_cast<char **>(&pos));
+
+  if (pos == field_value().c_str() || errno != 0) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace aos
diff --git a/aos/json_to_flatbuffer.fbs b/aos/json_to_flatbuffer.fbs
new file mode 100644
index 0000000..9743345
--- /dev/null
+++ b/aos/json_to_flatbuffer.fbs
@@ -0,0 +1,73 @@
+namespace aos.testing;
+
+table Location {
+  name:string;
+  type:string;
+  frequency:int;
+  max_size:int;
+}
+
+table Map {
+  match:Location;
+  rename:Location;
+}
+
+table Application {
+  name:string;
+  maps:[Map];
+}
+
+table Configuration {
+  locations:[Location] (id: 0);
+  maps:[Map] (id: 1);
+  applications:[Application] (id: 2);
+  imports:[string] (id: 3);
+
+  // 8 bit: byte ubyte bool
+  // 16 bit: short ushort
+  // 32 bit: int uint float
+  // 64 bit: long ulong double
+
+  // Simple values.
+  foo_byte:byte (id: 4);
+  foo_ubyte:ubyte (id: 5);
+  foo_bool:bool (id: 6);
+
+  foo_short:short (id: 7);
+  foo_ushort:ushort (id: 8);
+
+  foo_int:int (id: 9);
+  foo_uint:uint (id: 10);
+
+  foo_long:long (id: 11);
+  foo_ulong:ulong (id: 12);
+
+  foo_float:float (id: 13);
+  foo_double:double (id: 14);
+
+  foo_string:string (id: 15);
+
+  // Test vectors now.
+  vector_foo_byte:[byte] (id: 16);
+  vector_foo_ubyte:[ubyte] (id: 17);
+  vector_foo_bool:[bool] (id: 18);
+
+  vector_foo_short:[short] (id: 19);
+  vector_foo_ushort:[ushort] (id: 20);
+
+  vector_foo_int:[int] (id: 21);
+  vector_foo_uint:[uint] (id: 22);
+
+  vector_foo_long:[long] (id: 23);
+  vector_foo_ulong:[ulong] (id: 24);
+
+  vector_foo_float:[float] (id: 25);
+  vector_foo_double:[double] (id: 26);
+
+  vector_foo_string:[string] (id: 27);
+
+  // And a simple nested application.
+  single_application:Application (id: 28);
+}
+
+root_type Configuration;
diff --git a/aos/json_to_flatbuffer.h b/aos/json_to_flatbuffer.h
new file mode 100644
index 0000000..6deb663
--- /dev/null
+++ b/aos/json_to_flatbuffer.h
@@ -0,0 +1,110 @@
+#ifndef AOS_JSON_TO_FLATBUFFER_H_
+#define AOS_JSON_TO_FLATBUFFER_H_
+
+#include <cstddef>
+#include <string>
+
+#include "flatbuffers/flatbuffers.h"
+
+namespace aos {
+
+// Parses the flatbuffer into the vector, or returns an empty vector.
+::std::vector<uint8_t> JsonToFlatbuffer(
+    const char *data, const flatbuffers::TypeTable *typetable);
+
+// Converts a flatbuffer into a Json string.
+//
+// multi_line controls if the Json is written out on multiple lines or one.
+::std::string FlatbufferToJson(const uint8_t *buffer,
+                               const flatbuffers::TypeTable *typetable,
+                               bool multi_line = false);
+
+// This class implements the state machine at json.org
+class Tokenizer {
+ public:
+  Tokenizer(const char *data) : data_(data) {}
+
+  enum class TokenType {
+    kEnd,
+    kError,
+    kStartObject,
+    kEndObject,
+    kStartArray,
+    kEndArray,
+    kField,
+    kNumberValue,
+    kStringValue,
+    kTrueValue,
+    kFalseValue,
+  };
+
+  // Returns the next token.
+  TokenType Next();
+
+  // Returns the last field_name and field_value.  These are only valid when
+  // Next returns them.
+  const ::std::string &field_name() const { return field_name_; }
+  const ::std::string &field_value() const { return field_value_; }
+
+  // Parses the current field value as a long long.  Returns false if it failed
+  // to parse.
+  bool FieldAsInt(long long *value);
+  // Parses the current field value as a double.  Returns false if it failed
+  // to parse.
+  bool FieldAsDouble(double *value);
+
+  // Returns true if we are at the end of the input.
+  bool AtEnd() { return *data_ == '\0'; }
+
+  const char *data_left() const { return data_; }
+
+ private:
+  // Consumes a string out of data_.  Populates s with the string.  Returns true
+  // if a valid string was found, and false otherwise.
+  // data_ is updated only on success.
+  bool ConsumeString(::std::string *s);
+  // Consumes a number out of data_.  Populates s with the string containing the
+  // number.  Returns true if a valid number was found, and false otherwise.
+  // data_ is updated only on success.
+  bool ConsumeNumber(::std::string *s);
+  // Consumes a fixed token out of data_. Returns true if the string was found,
+  // and false otherwise.
+  // data_ is updated only on success.
+  bool Consume(const char* token);
+  // Consumes whitespace out of data_. Returns true if the string was found,
+  // and false otherwise.
+  // data_ is unconditionally updated.
+  void ConsumeWhitespace();
+
+  // State for the parsing state machine.
+  enum class State {
+    kExpectField,
+    kExpectObjectStart,
+    kExpectObjectEnd,
+    kExpectArrayEnd,
+    kExpectValue,
+    kExpectEnd,
+  };
+
+  State state_ = State::kExpectObjectStart;
+
+  // Data pointer.
+  const char *data_;
+  // Current line number used for printing debug.
+  int linenumber_ = 0;
+
+  // Stack used to track which object type we were in when we recursed.
+  enum class ObjectType {
+    kObject,
+    kArray,
+  };
+  ::std::vector<ObjectType> object_type_;
+
+  // Last field name.
+  ::std::string field_name_;
+  // Last field value.
+  ::std::string field_value_;
+};
+}  // namespace aos
+
+#endif  // AOS_JSON_TO_FLATBUFFER_H_
diff --git a/aos/json_to_flatbuffer_test.cc b/aos/json_to_flatbuffer_test.cc
new file mode 100644
index 0000000..37edbf3
--- /dev/null
+++ b/aos/json_to_flatbuffer_test.cc
@@ -0,0 +1,139 @@
+#include "aos/json_to_flatbuffer.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/json_to_flatbuffer_generated.h"
+#include "flatbuffers/minireflect.h"
+
+namespace aos {
+namespace testing {
+
+class JsonToFlatbufferTest : public ::testing::Test {
+ public:
+  JsonToFlatbufferTest() {}
+
+  bool JsonAndBack(const ::std::string str) { return JsonAndBack(str, str); }
+
+  bool JsonAndBack(const ::std::string in, const ::std::string out) {
+    printf("Testing: %s\n", in.c_str());
+    const ::std::vector<uint8_t> fb =
+        JsonToFlatbuffer(in.data(), ConfigurationTypeTable());
+
+    if (fb.size() == 0) {
+      return false;
+    }
+
+    const ::std::string back =
+        FlatbufferToJson(fb.data(), ConfigurationTypeTable());
+
+    printf("Back to string: %s\n", back.c_str());
+
+    return back == out;
+  }
+};
+
+// Tests that the various escapes work as expected.
+TEST_F(JsonToFlatbufferTest, ValidEscapes) {
+  EXPECT_TRUE(
+      JsonAndBack("{ \"foo_string\": \"a\\\"b\\/c\\bd\\fc\\nd\\re\\tf\" }",
+                  "{ \"foo_string\": \"a\\\"b/c\\bd\\fc\\nd\\re\\tf\" }"));
+}
+
+// Test the easy ones.  Test every type, single, no nesting.
+TEST_F(JsonToFlatbufferTest, Basic) {
+  EXPECT_TRUE(JsonAndBack("{ \"foo_bool\": true }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"foo_byte\": 5 }"));
+  EXPECT_TRUE(JsonAndBack("{ \"foo_ubyte\": 5 }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"foo_short\": 5 }"));
+  EXPECT_TRUE(JsonAndBack("{ \"foo_ushort\": 5 }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"foo_int\": 5 }"));
+  EXPECT_TRUE(JsonAndBack("{ \"foo_uint\": 5 }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"foo_long\": 5 }"));
+  EXPECT_TRUE(JsonAndBack("{ \"foo_ulong\": 5 }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"foo_float\": 5.0 }"));
+  EXPECT_TRUE(JsonAndBack("{ \"foo_double\": 5.0 }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"foo_string\": \"baz\" }"));
+}
+
+// Test what happens if you pass a field name that we don't know.
+TEST_F(JsonToFlatbufferTest, InvalidFieldName) {
+  EXPECT_FALSE(JsonAndBack("{ \"foo\": 5 }"));
+}
+
+// Test that adding a duplicate field results in an error.
+TEST_F(JsonToFlatbufferTest, DuplicateField) {
+  EXPECT_FALSE(
+      JsonAndBack("{ \"foo_int\": 5, \"foo_int\": 7 }", "{ \"foo_int\": 7 }"));
+}
+
+// Test that various syntax errors are caught correctly
+TEST_F(JsonToFlatbufferTest, InvalidSyntax) {
+  EXPECT_FALSE(JsonAndBack("{ \"foo_int\": 5"));
+  EXPECT_FALSE(JsonAndBack("{ \"foo_int\": 5 "));
+  EXPECT_FALSE(JsonAndBack("{ \"foo_string\": \""));
+  EXPECT_FALSE(JsonAndBack("{ \"foo_int\": 5 } }"));
+
+  EXPECT_FALSE(JsonAndBack("{ foo_int: 5 }"));
+
+  EXPECT_FALSE(JsonAndBack("{ \"foo_int\": 5, }", "{ \"foo_int\": 5 }"));
+
+  EXPECT_FALSE(JsonAndBack(
+      "{ \"applications\":\n[\n{\n\"name\": \"woot\"\n},\n{\n\"name\": "
+      "\"wow\"\n} ,\n]\n}"));
+
+  EXPECT_FALSE(
+      JsonAndBack("{ \"applications\": [ { \"name\": \"woot\" }, { \"name\": "
+                  "\"wow\" } ] , }"));
+
+  EXPECT_FALSE(
+      JsonAndBack("{ \"vector_foo_string\": [ \"bar\", \"baz\" ] , }"));
+
+  EXPECT_FALSE(
+      JsonAndBack("{ \"single_application\": { \"name\": \"woot\" } , }"));
+}
+
+// Test arrays of simple types.
+TEST_F(JsonToFlatbufferTest, Array) {
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_byte\": [ 9, 7, 1 ] }"));
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_ubyte\": [ 9, 7, 1 ] }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_short\": [ 9, 7, 1 ] }"));
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_ushort\": [ 9, 7, 1 ] }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_int\": [ 9, 7, 1 ] }"));
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_uint\": [ 9, 7, 1 ] }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_long\": [ 9, 7, 1 ] }"));
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_ulong\": [ 9, 7, 1 ] }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_float\": [ 9.0, 7.0, 1.0 ] }"));
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_float\": [ 9, 7, 1 ] }",
+                          "{ \"vector_foo_float\": [ 9.0, 7.0, 1.0 ] }"));
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_double\": [ 9, 7, 1 ] }",
+                          "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+
+  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_string\": [ \"bar\", \"baz\" ] }"));
+}
+
+// Test nested messages, and arrays of nested messages.
+TEST_F(JsonToFlatbufferTest, NestedStruct) {
+  EXPECT_TRUE(
+      JsonAndBack("{ \"single_application\": { \"name\": \"woot\" } }"));
+
+  EXPECT_TRUE(
+      JsonAndBack("{ \"applications\": [ { \"name\": \"woot\" }, { \"name\": "
+                  "\"wow\" } ] }"));
+}
+
+// TODO(austin): Missmatched values.
+
+}  // namespace testing
+}  // namespace aos
diff --git a/third_party/flatbuffers/BUILD b/third_party/flatbuffers/BUILD
index 520f98a..17f5b8c 100644
--- a/third_party/flatbuffers/BUILD
+++ b/third_party/flatbuffers/BUILD
@@ -27,6 +27,7 @@
         "src/util.cpp",
     ],
     hdrs = [":public_headers"],
+    copts = ["-Wno-cast-align"],
     includes = ["include/"],
     linkstatic = 1,
 )
@@ -86,11 +87,11 @@
         "src/idl_gen_cpp.cpp",
         "src/idl_gen_dart.cpp",
         "src/idl_gen_general.cpp",
-        "src/idl_gen_kotlin.cpp",
         "src/idl_gen_go.cpp",
         "src/idl_gen_grpc.cpp",
         "src/idl_gen_js_ts.cpp",
         "src/idl_gen_json_schema.cpp",
+        "src/idl_gen_kotlin.cpp",
         "src/idl_gen_lobster.cpp",
         "src/idl_gen_lua.cpp",
         "src/idl_gen_php.cpp",
@@ -137,8 +138,8 @@
         "src/util.cpp",
         "tests/namespace_test/namespace_test1_generated.h",
         "tests/namespace_test/namespace_test2_generated.h",
-        "tests/native_type_test_impl.h",
         "tests/native_type_test_impl.cpp",
+        "tests/native_type_test_impl.h",
         "tests/test.cpp",
         "tests/test_assert.cpp",
         "tests/test_assert.h",
@@ -152,12 +153,18 @@
         "-DBAZEL_TEST_DATA_PATH",
     ],
     data = [
+        ":tests/arrays_test.bfbs",
+        ":tests/arrays_test.fbs",
+        ":tests/arrays_test.golden",
         ":tests/include_test/include_test1.fbs",
         ":tests/include_test/sub/include_test2.fbs",
+        ":tests/monster_extra.fbs",
         ":tests/monster_test.bfbs",
         ":tests/monster_test.fbs",
+        ":tests/monsterdata_extra.json",
         ":tests/monsterdata_test.golden",
         ":tests/monsterdata_test.json",
+        ":tests/native_type_test.fbs",
         ":tests/prototest/imported.proto",
         ":tests/prototest/test.golden",
         ":tests/prototest/test.proto",
@@ -165,21 +172,15 @@
         ":tests/unicode_test.json",
         ":tests/union_vector/union_vector.fbs",
         ":tests/union_vector/union_vector.json",
-        ":tests/monster_extra.fbs",
-        ":tests/monsterdata_extra.json",
-        ":tests/arrays_test.bfbs",
-        ":tests/arrays_test.fbs",
-        ":tests/arrays_test.golden",
-        ":tests/native_type_test.fbs",
     ],
     includes = [
         "include/",
         "tests/",
     ],
     deps = [
+        ":arrays_test_cc_fbs",
         ":monster_extra_cc_fbs",
         ":monster_test_cc_fbs",
-        ":arrays_test_cc_fbs",
         ":native_type_test_cc_fbs",
     ],
 )
@@ -211,7 +212,8 @@
         "--gen-mutable",
         "--reflect-names",
         "--cpp-ptr-type flatbuffers::unique_ptr",
-        "--scoped-enums" ],
+        "--scoped-enums",
+    ],
 )
 
 flatbuffer_cc_library(
@@ -220,6 +222,6 @@
     flatc_args = [
         "--gen-object-api",
         "--gen-mutable",
-        "--cpp-ptr-type flatbuffers::unique_ptr" ],
+        "--cpp-ptr-type flatbuffers::unique_ptr",
+    ],
 )
-
diff --git a/third_party/flatbuffers/include/flatbuffers/minireflect.h b/third_party/flatbuffers/include/flatbuffers/minireflect.h
index 9d648ec..e7b9024 100644
--- a/third_party/flatbuffers/include/flatbuffers/minireflect.h
+++ b/third_party/flatbuffers/include/flatbuffers/minireflect.h
@@ -396,7 +396,7 @@
                                       const TypeTable *type_table,
                                       bool multi_line = false,
                                       bool vector_delimited = true) {
-  ToStringVisitor tostring_visitor(multi_line ? "\n" : " ", false, "",
+  ToStringVisitor tostring_visitor(multi_line ? "\n" : " ", true, "",
                                    vector_delimited);
   IterateFlatBuffer(buffer, type_table, &tostring_visitor);
   return tostring_visitor.s;
diff --git a/third_party/jsont/BUILD b/third_party/jsont/BUILD
new file mode 100644
index 0000000..dc1d03d
--- /dev/null
+++ b/third_party/jsont/BUILD
@@ -0,0 +1,28 @@
+licenses(["notice"])
+
+cc_library(
+    name = "jsont",
+    srcs = [
+        "jsont.c",
+    ],
+    hdrs = [
+        "jsont.h",
+    ],
+    includes = ["."],
+    visibility = ["//visibility:public"],
+)
+
+cc_test(
+    name = "jsont_test",
+    srcs = ["test/test_tokenizer.c"],
+    copts = [
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+    ] + select({
+        "@//tools:cpu_roborio": [
+            "-Wno-unused-but-set-variable",
+        ],
+        "//conditions:default": [],
+    }),
+    deps = [":jsont"],
+)
diff --git a/third_party/jsont/jsont.c b/third_party/jsont/jsont.c
index 5863c7a..b318494 100644
--- a/third_party/jsont/jsont.c
+++ b/third_party/jsont/jsont.c
@@ -74,7 +74,7 @@
   for (size_t i = 0; i != len; ++i) {
     uint8_t b = bytes[i];
     int digit = (b > '0'-1 && b < 'f'+1) ? kHexValueTable[b-'0'] : -1;
-    if (b == -1 || // bad digit
+    if (b == 0xff || // bad digit
         (value > cutoff) || // overflow
         ((value == cutoff) && (digit > cutoff_digit)) ) {
       return ULONG_MAX;
@@ -172,9 +172,10 @@
       (memcmp((const void*)ctx->value_buf.data,
         (const void*)bytes, length) == 0);
   } else {
-    return (ctx->input_buf_value_end - ctx->input_buf_value_start == length) &&
-      (memcmp((const void*)ctx->input_buf_value_start,
-        (const void*)bytes, length) == 0);
+    return (ctx->input_buf_value_end - ctx->input_buf_value_start ==
+            (ssize_t)length) &&
+           (memcmp((const void *)ctx->input_buf_value_start,
+                   (const void *)bytes, length) == 0);
   }
 }