JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v,
                          std::list<std::string>& errors,
                          std::string prefix)
{
    JSON_array* this_arr = dynamic_cast<JSON_array*>(this_v);
    JSON_dictionary* this_dict = dynamic_cast<JSON_dictionary*>(this_v);

    JSON_array* sch_arr = dynamic_cast<JSON_array*>(sch_v);
    JSON_dictionary* sch_dict = dynamic_cast<JSON_dictionary*>(sch_v);

    std::string err_prefix;
    if (prefix.empty())
    {
        err_prefix = "top-level object";
    }
    else
    {
        err_prefix = "json key \"" + prefix + "\"";
    }

    if (sch_dict)
    {
        if (! this_dict)
        {
            QTC::TC("libtests", "JSON wanted dictionary");
            errors.push_back(err_prefix + " is supposed to be a dictionary");
            return false;
        }
        for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter =
                 sch_dict->members.begin();
             iter != sch_dict->members.end(); ++iter)
        {
            std::string const& key = (*iter).first;
            if (this_dict->members.count(key))
            {
                checkSchemaInternal(
                    this_dict->members[key].getPointer(),
                    (*iter).second.getPointer(),
                    errors, prefix + "." + key);
            }
            else
            {
                QTC::TC("libtests", "JSON key missing in object");
                errors.push_back(
                    err_prefix + ": key \"" + key +
                    "\" is present in schema but missing in object");
            }
        }
        for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter =
                 this_dict->members.begin();
             iter != this_dict->members.end(); ++iter)
        {
            std::string const& key = (*iter).first;
            if (sch_dict->members.count(key) == 0)
            {
                QTC::TC("libtests", "JSON key extra in object");
                errors.push_back(
                    err_prefix + ": key \"" + key +
                    "\" is not present in schema but appears in object");
            }
        }
    }
    else if (sch_arr)
    {
        if (! this_arr)
        {
            QTC::TC("libtests", "JSON wanted array");
            errors.push_back(err_prefix + " is supposed to be an array");
            return false;
        }
        if (sch_arr->elements.size() != 1)
        {
            QTC::TC("libtests", "JSON schema array error");
            errors.push_back(err_prefix +
                             " schema array contains other than one item");
            return false;
        }
        int i = 0;
        for (std::vector<PointerHolder<JSON_value> >::iterator iter =
                 this_arr->elements.begin();
             iter != this_arr->elements.end(); ++iter, ++i)
        {
            checkSchemaInternal(
                (*iter).getPointer(),
                sch_arr->elements.at(0).getPointer(),
                errors, prefix + "." + QUtil::int_to_string(i));
        }
    }

    return errors.empty();
}
JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v,
                          std::list<std::string>& errors,
                          std::string prefix)
{
    JSON_array* this_arr = dynamic_cast<JSON_array*>(this_v);
    JSON_dictionary* this_dict = dynamic_cast<JSON_dictionary*>(this_v);

    JSON_array* sch_arr = dynamic_cast<JSON_array*>(sch_v);
    JSON_dictionary* sch_dict = dynamic_cast<JSON_dictionary*>(sch_v);

    std::string err_prefix;
    if (prefix.empty())
    {
        err_prefix = "top-level object";
    }
    else
    {
        err_prefix = "json key \"" + prefix + "\"";
    }

    if (sch_dict)
    {
        if (! this_dict)
        {
            QTC::TC("libtests", "JSON wanted dictionary");
            errors.push_back(err_prefix + " is supposed to be a dictionary");
            return false;
        }
        for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter =
                 sch_dict->members.begin();
             iter != sch_dict->members.end(); ++iter)
        {
            std::string const& key = (*iter).first;
            if (this_dict->members.count(key))
            {
                checkSchemaInternal(
                    this_dict->members[key].getPointer(),
                    (*iter).second.getPointer(),
                    errors, prefix + "." + key);
            }
            else
            {
                QTC::TC("libtests", "JSON key missing in object");
                errors.push_back(
                    err_prefix + ": key \"" + key +
                    "\" is present in schema but missing in object");
            }
        }
        for (std::map<std::string, PointerHolder<JSON_value> >::iterator iter =
                 this_dict->members.begin();
             iter != this_dict->members.end(); ++iter)
        {
            std::string const& key = (*iter).first;
            if (sch_dict->members.count(key) == 0)
            {
                QTC::TC("libtests", "JSON key extra in object");
                errors.push_back(
                    err_prefix + ": key \"" + key +
                    "\" is not present in schema but appears in object");
            }
        }
    }
    else if (sch_arr)
    {
        if (! this_arr)
        {
            QTC::TC("libtests", "JSON wanted array");
            errors.push_back(err_prefix + " is supposed to be an array");
            return false;
        }
        if (sch_arr->elements.size() != 1)
        {
            QTC::TC("libtests", "JSON schema array error");
            errors.push_back(err_prefix +
                             " schema array contains other than one item");
            return false;
        }
        int i = 0;
        for (std::vector<PointerHolder<JSON_value> >::iterator iter =
                 this_arr->elements.begin();
             iter != this_arr->elements.end(); ++iter, ++i)
        {
            checkSchemaInternal(
                (*iter).getPointer(),
                sch_arr->elements.at(0).getPointer(),
                errors, prefix + "." + QUtil::int_to_string(i));
        }
    }

    return errors.empty();
}