aboutsummaryrefslogtreecommitdiff
path: root/include/jsoncons_ext/jsonschema/subschema.hpp
blob: cbe0af4b8ebeddb90f97501708a9e0dc14ef412e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2020 Daniel Parker
// Distributed under the Boost license, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// See https://github.com/danielaparker/jsoncons for latest version

#ifndef JSONCONS_JSONSCHEMA_SUBSCHEMA_HPP
#define JSONCONS_JSONSCHEMA_SUBSCHEMA_HPP

#include <jsoncons/config/jsoncons_config.hpp>
#include <jsoncons/uri.hpp>
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
#include <jsoncons_ext/jsonschema/jsonschema_error.hpp>
#include <jsoncons_ext/jsonschema/schema_location.hpp>

namespace jsoncons {
namespace jsonschema {

    // Interface for validation error handlers
    class error_reporter
    {
        bool fail_early_;
        std::size_t error_count_;
    public:
        error_reporter(bool fail_early = false)
            : fail_early_(fail_early), error_count_(0)
        {
        }

        virtual ~error_reporter() = default;

        void error(const validation_output& o)
        {
            ++error_count_;
            do_error(o);
        }

        std::size_t error_count() const
        {
            return error_count_;
        }

        bool fail_early() const
        {
            return fail_early_;
        }

    private:
        virtual void do_error(const validation_output& /* e */) = 0;
    };

    template <class Json>
    class keyword_validator 
    {
        std::string absolute_keyword_location_;
    public:
        using self_pointer = keyword_validator<Json>*;

        keyword_validator(const std::string& absolute_keyword_location)
            : absolute_keyword_location_(absolute_keyword_location)
        {
        }

        keyword_validator(const keyword_validator&) = delete;
        keyword_validator(keyword_validator&&) = default;
        keyword_validator& operator=(const keyword_validator&) = delete;
        keyword_validator& operator=(keyword_validator&&) = default;

        virtual ~keyword_validator() = default;

        const std::string& absolute_keyword_location() const
        {
            return absolute_keyword_location_;
        }

        void validate(const Json& instance, 
                      const jsonpointer::json_pointer& instance_location, 
                      error_reporter& reporter, 
                      Json& patch) const 
        {
            do_validate(instance, 
                        instance_location,
                        reporter,
                        patch);
        }

        virtual jsoncons::optional<Json> get_default_value(const jsonpointer::json_pointer&, const Json&, error_reporter&) const
        {
            return jsoncons::optional<Json>();
        }

    private:
        virtual void do_validate(const Json& instance, 
                                 const jsonpointer::json_pointer& instance_location, 
                                 error_reporter& reporter, 
                                 Json& patch) const = 0;
    };

    template <class Json>
    std::vector<schema_location> update_uris(const Json& schema,
                                         const std::vector<schema_location>& uris,
                                         const std::vector<std::string>& keys)
    {
        // Exclude uri's that are not plain name identifiers
        std::vector<schema_location> new_uris;
        for (const auto& uri : uris)
        {
            if (!uri.has_identifier())
                new_uris.push_back(uri);
        }

        // Append the keys for this sub-schema to the uri's
        for (const auto& key : keys)
        {
            for (auto& uri : new_uris)
            {
                auto new_u = uri.append(key);
                uri = schema_location(new_u);
            }
        }
        if (schema.type() == json_type::object_value)
        {
            auto it = schema.find("$id"); // If $id is found, this schema can be referenced by the id
            if (it != schema.object_range().end()) 
            {
                std::string id = it->value().template as<std::string>(); 
                // Add it to the list if it is not already there
                if (std::find(new_uris.begin(), new_uris.end(), id) == new_uris.end())
                {
                    schema_location relative(id); 
                    schema_location new_uri = relative.resolve(new_uris.back());
                    new_uris.emplace_back(new_uri); 
                }
            }
        }

        return new_uris;
    }

} // namespace jsonschema
} // namespace jsoncons

#endif // JSONCONS_JSONSCHEMA_RULE_HPP