├── jsonschemacodegen
├── templates
│ ├── cpp
│ │ ├── __init__.py
│ │ ├── propname.jinja2
│ │ ├── ref.hpp.jinja2
│ │ ├── source.cpp.jinja2
│ │ ├── constraints.jinja2
│ │ ├── null.cpp.jinja2
│ │ ├── header.hpp.jinja2
│ │ ├── bool.cpp.jinja2
│ │ ├── loader.jinja2
│ │ ├── null.hpp.jinja2
│ │ ├── bool.hpp.jinja2
│ │ ├── datetime.cpp.jinja2
│ │ ├── string_enum.cpp.jinja2
│ │ ├── anyof.cpp.jinja2
│ │ ├── array.cpp.jinja2
│ │ ├── numeric.cpp.jinja2
│ │ ├── anyof.hpp.jinja2
│ │ ├── array.hpp.jinja2
│ │ ├── string.cpp.jinja2
│ │ ├── allof.cpp.jinja2
│ │ ├── string_enum.hpp.jinja2
│ │ ├── oneof.cpp.jinja2
│ │ ├── oneof.hpp.jinja2
│ │ ├── object.hpp.jinja2
│ │ ├── allof.hpp.jinja2
│ │ ├── datetime.hpp.jinja2
│ │ ├── numeric.hpp.jinja2
│ │ ├── string.hpp.jinja2
│ │ ├── object.cpp.jinja2
│ │ └── exceptions.hpp.jinja2
│ ├── markdown
│ │ ├── __init__.py
│ │ ├── null.md.jinja2
│ │ ├── boolean.md.jinja2
│ │ ├── oneOf.md.jinja2
│ │ ├── description.md.jinja2
│ │ ├── anyOf.md.jinja2
│ │ ├── object.md.jinja2
│ │ ├── number.md.jinja2
│ │ ├── allOf.md.jinja2
│ │ ├── schema.jinja2
│ │ ├── string.md.jinja2
│ │ ├── integer.md.jinja2
│ │ └── array.md.jinja2
│ └── python
│ │ ├── __init__.py
│ │ ├── reference.py.jinja2
│ │ ├── null.py.jinja2
│ │ ├── file.py.jinja2
│ │ ├── test.py.jinja2
│ │ ├── loader.jinja2
│ │ ├── array.py.jinja2
│ │ ├── object.py.jinja2
│ │ ├── oneof.py.jinja2
│ │ ├── import.py.jinja2
│ │ ├── axxof.py.jinja2
│ │ └── primative.py.jinja2
├── _version.py
├── __init__.py
├── json_example.py
├── python.py
├── resolver.py
└── cpp.py
├── requirements.txt
├── .gitpod.yml
├── .gitignore
├── MANIFEST.in
├── .gitpod.Dockerfile
├── setup.py
├── examples
├── example_markdown.py
├── example_json.py
├── example_asyncapi.py
├── example_python.py
└── example_usage.py
├── README.md
└── LICENSE
/jsonschemacodegen/templates/cpp/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | stringcase
2 | jacobs-jinja-too>=0.0.4
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | image:
2 | file: .gitpod.Dockerfile
3 |
4 | tasks:
5 | - init: pip install -r ./requirements.txt
6 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/null.md.jinja2:
--------------------------------------------------------------------------------
1 | **null**{%if 'title' in schema%} - {{schema.title|italics}}{%endif%}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | output/
3 | *.egg-info/
4 | dist/
5 | build/
6 | .vscode/
7 | *~
8 | *.pyc
9 | .DS_Store
10 |
--------------------------------------------------------------------------------
/jsonschemacodegen/_version.py:
--------------------------------------------------------------------------------
1 | __version_info__ = (0, 6, 3)
2 | __version__ = ".".join([str(x) for x in list(__version_info__)])
3 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/propname.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro ConstPropertyName(propname)-%}
2 | PROPERTY_NAME_{{propname|CONST_CASE}}
3 | {%-endmacro-%}
--------------------------------------------------------------------------------
/jsonschemacodegen/__init__.py:
--------------------------------------------------------------------------------
1 | from . import _version
2 |
3 | __version__ = _version.__version__
4 | __version_info__ = _version.__version_info__
5 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md
2 | exclude *.git*
3 | global-exclude *.pyc
4 | global-exclude __pycache__
5 | recursive-include templates *.jinja2
6 | include LICENSE
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/boolean.md.jinja2:
--------------------------------------------------------------------------------
1 | **boolean**{%if 'title' in schema%} - *{{schema.title}}*{%endif%}
2 | {%-if 'default' in schema%}
3 | * Default value: `{{schema.default | lower }}`
4 | {%-endif-%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/reference.py.jinja2:
--------------------------------------------------------------------------------
1 | {{resolver.py_include_statement(schema['$ref'])}}
2 | {%-set itemType = resolver.py_class_name(schema['$ref'])%}
3 |
4 | class {{Name}} ({{itemType}}):
5 | pass
6 |
7 |
--------------------------------------------------------------------------------
/.gitpod.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gitpod/workspace-full
2 |
3 | # Install custom tools, runtimes, etc.
4 | # For example "bastet", a command-line tetris clone:
5 | # RUN brew install bastet
6 | #
7 | # More information: https://www.gitpod.io/docs/config-docker/
8 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/ref.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader-%}
2 | {%-set refName = loader.Reference(resolver, schema['$ref'])-%}
3 | /*! {{Name}} is an alias of {{refName}}.
4 | */
5 | using {{Name}} = {{refName}};
6 |
7 | {{''}}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/oneOf.md.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'schema.jinja2' as loader-%}
2 | One of the following types:
3 | {%- for s in schema.GetComponents() %}
4 | * OR {{loader.Display(s.Resolve(resolver), resolver) | mdindent(2)}}
5 | {# #}
6 | {%-endfor%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/description.md.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'schema.jinja2' as loader-%}
2 | {%-set resolvedSchema = schema.Resolve(resolver)-%}
3 | {{loader.Display(resolvedSchema, resolver)}}
4 | {%if 'description' in resolvedSchema%}{{resolvedSchema.description}}{%endif%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/anyOf.md.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'schema.jinja2' as loader-%}
2 | An **object**{%if 'title' in schema%} - {{schema.title|italics}}. It is {%endif%} made up of properties from one or more of these groups:
3 | {%for s in schema.GetComponents() %}
4 | {{loader.Display(s.Resolve(resolver), resolver)}}
5 | {%endfor%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/null.py.jinja2:
--------------------------------------------------------------------------------
1 | class {{Name}}(object):
2 | """ This represents a JSON NULL value.
3 | """
4 |
5 | def __init__(self, data=None):
6 | if not isinstance(data, type(self)) and data is not None:
7 | raise ValueError("Expected None")
8 |
9 | def Get(self) -> None:
10 | return None
11 |
12 | def Serializable(self):
13 | return self.Get()
14 | {# #}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/object.md.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'schema.jinja2' as loader-%}
2 | **Object**{%if 'title' in schema%} - {{schema.title | italics}}. Has{%else%} with{%endif%} these properties:
3 |
4 | | Property Name | Type | Description |
5 | {%-for propName, prop in schema.properties.items() %}
6 | {{loader.Property(propName, prop.Resolve(resolver), propName in schema.required or propName in schema.requiredProperties, resolver)}}
7 | {%-endfor%}
8 |
9 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/source.cpp.jinja2:
--------------------------------------------------------------------------------
1 | // This file is automatically generated using a JSON-Schema definition.
2 | // DO NOT MODIFY this file, as changes will be overwritten on the next code
3 | // generation.
4 | {{''}}
5 | {%-for dep in deps %}
6 | #include {{dep}}
7 | {%-endfor%}
8 |
9 | {%-set originalNamespace=ns%}
10 | {%for using in resolver.cpp_get_usings() %}
11 | using namespace {{using | join("::") }};
12 | {%-endfor%}
13 |
14 | {%-from 'loader.jinja2' import Class with context%}
15 | {{-Class('cpp', resolver, ns, Name, schema)}}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/file.py.jinja2:
--------------------------------------------------------------------------------
1 | {%-if '$ref' in schema %}
2 | {%-include 'reference.py.jinja2'-%}
3 | {%-else%}
4 | {%-import 'loader.jinja2' as loader-%}
5 | {%-import 'import.py.jinja2' as importer-%}
6 | {%- set includes = []-%}
7 | {%- set include_set = []-%}
8 | {{importer.GetImports(resolver, schema, includes)}}
9 | {%-for include in includes-%}
10 | {%-if include not in include_set-%}
11 | {%-do include_set.append(include)-%}
12 | {{include + "\n"}}
13 | {%-endif-%}
14 | {%-endfor%}
15 | {{loader.Class(resolver, Name, schema)}}
16 | {%-endif-%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/number.md.jinja2:
--------------------------------------------------------------------------------
1 | **number**{%if 'title' in schema%} - {{schema.title|italics}}{%endif%}
2 | {%-if 'default' in schema%}
3 | * Default value: `{{schema.default}}`
4 | {%-endif-%}
5 | {%-if 'minimum' in schema%}
6 | * Greater than or equal to {{schema.minimum}}
7 | {%-endif-%}
8 | {%if 'maximum' in schema%}
9 | * Less than or equal to {{schema.maximum}}
10 | {%-endif-%}
11 | {%if 'exclusiveMinimum' in schema%}
12 | * Greater than {{schema.exclusiveMinimum}}
13 | {%-endif-%}
14 | {%if 'exclusiveMaximum' in schema%}
15 | * Less than {{schema.maximum}}
16 | {%-endif-%}
17 | {%if 'multipleOf' in schema%}
18 | * Multiple of {{schema.multipleOf}}
19 | {%-endif-%}
20 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/constraints.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro ExprName(constraint)-%}
2 | {%-if constraint == 'default'-%}
3 | VALUE_DEFAULT
4 | {%-elif constraint == 'minimum'-%}
5 | VALUE_MINIMUM
6 | {%-elif constraint == 'maximum'-%}
7 | VALUE_MAXIMUM
8 | {%-elif constraint == 'exclusiveMinimum'-%}
9 | VALUE_EXCLUSIVE_MINIMUM
10 | {%-elif constraint == 'exclusiveMaximum'-%}
11 | VALUE_EXCLUSIVE_MAXIMUM
12 | {%-elif constraint == 'multipleOf'-%}
13 | VALUE_MULTIPLE_OF
14 | {%-elif constraint == 'const'-%}
15 | VALUE_CONSTANT
16 | {%-else-%}
17 | VALUE_{{constraint | CONST_CASE}}
18 | {%-endif-%}
19 | {%-endmacro-%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/allOf.md.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'schema.jinja2' as loader-%}
2 | **Object**{%if 'title' in schema%} - {{schema.title |italics}}. Has{%else%} with{%endif%} these properties:
3 |
4 | | Property Name | Type | |
5 | {%-for s in schema.GetComponents() %}
6 | {%-set subSchema = s.Resolve(resolver).Resolve(resolver)%}
7 | {%-if 'properties' in subSchema%}
8 | {%-for propName, prop in subSchema.properties.items() %}
9 | {{loader.Property(propName, prop.Resolve(resolver), propName in subSchema.required or propName in subSchema.requiredProperties, resolver)}}
10 | {%-endfor%}
11 | {%-else-%}
12 | |
13 |
14 |
15 | {{loader.Display(subSchema, resolver)}}
16 |
17 |
18 | |
19 | {%-endif-%}
20 | {%endfor%}
21 |
22 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/schema.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro Display(schema, resolver=None) %}
2 | {%-if schema.always_valid is defined -%}
3 | {%if schema.always_valid %}Can be anything{%else%}🚫 Must not be present{%endif%}.
4 | {%-elif schema.oneOf is defined -%}
5 | {%-include 'oneOf.md.jinja2'-%}
6 | {%-elif schema.allOf is defined -%}
7 | {%-include 'allOf.md.jinja2'-%}
8 | {%-elif schema.anyOf is defined -%}
9 | {%-include 'anyOf.md.jinja2'-%}
10 | {%-elif 'type' in schema %}
11 | {%-include schema.type+'.md.jinja2'-%}
12 | {%-else-%}
13 | {{schema}}
14 | {%-endif-%}
15 | {%-endmacro-%}
16 | {#- -#}
17 | {%-macro Property(propName, prop, required=False, resolver=None)%}
18 |
19 | |
20 |
21 | **{{propName}}** {%if required %}(required){%endif%}
22 |
23 | {%if 'title' in prop%}*{{prop.title}}*
24 | {%endif%}
25 | |
26 |
27 |
28 | {{Display(prop, resolver)}}
29 |
30 | |
31 |
32 | {{prop.description}}
33 |
34 | |
35 |
36 | {%-endmacro-%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/null.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
2 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
3 | {{className}}::{{Name}}()
4 | {
5 | }
6 |
7 | boost::none_t {{className}}::Get() const
8 | {
9 | return boost::none;
10 | }
11 |
12 | {{className}} {{className}}::FromJson(const {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& json)
13 | {
14 | if (!(json.IsNull()))
15 | {
16 | throw {{exception}}("Not the NULL value");
17 | }
18 |
19 | return {{className}}();
20 | }
21 |
22 | void {{className}}::ToJson({{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
23 | {
24 | value.SetNull();
25 | }
26 |
27 | void {{className}}::SetHandle(const std::string& handle)
28 | {
29 | _handle = handle;
30 | }
31 |
32 | std::string {{className}}::GetHandle() const
33 | {
34 | return _handle;
35 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/test.py.jinja2:
--------------------------------------------------------------------------------
1 | import unittest
2 | import json
3 | {{resolver.py_include_statement(path)}}
4 |
5 | class Test{{Name|UpperCamelCase}}{{objType|UpperCamelCase}}Structure(unittest.TestCase):
6 | """A test for the following schema:
7 | {{schema}}
8 | """
9 |
10 | def do_{{Name|snake_case}}_load(self, json_example_string):
11 | ex = json.loads(json_example_string)
12 | {{resolver.py_class_name(path)}}(ex)
13 |
14 | def test_parse_json(self):
15 | examples = [{%for example in examples%}
16 | ## Example Index {{loop.index - 1}}
17 | '{{example}}',
18 | {%endfor%}]
19 | for i, ex_str in enumerate(examples):
20 | with self.subTest(index=i):
21 | self.do_{{Name|snake_case}}_load(ex_str)
22 |
23 | def test_fail_when_not_json(self):
24 | example = 'this string here should cause it to fail{{examples[-1]}}'
25 | with self.assertRaises(ValueError):
26 | self.do_{{Name|snake_case}}_load(example)
27 | {# #}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/loader.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro Class(resolver, Name, schema, parentClass=None) -%}
2 | {%-if schema.oneOf is defined -%}
3 | {%-include 'oneof.py.jinja2'-%}
4 | {%-elif schema.allOf is defined -%}
5 | {%-include 'axxof.py.jinja2'-%}
6 | {%-elif schema.anyOf is defined -%}
7 | {%-include 'axxof.py.jinja2'-%}
8 | {%-elif '$ref' in schema %}
9 | # This object makes use of an external schema specified at {{schema['$ref']}}
10 | {%-elif 'type' not in schema-%}
11 | {%-elif schema.type == 'integer'
12 | or schema.type == 'number'
13 | or schema.type == 'boolean'
14 | or schema.type == 'string'-%}
15 | {%-include 'primative.py.jinja2'-%}
16 | {%-elif schema.type == 'array'-%}
17 | {%-include 'array.py.jinja2'-%}
18 | {%-elif schema.type == 'object'-%}
19 | {%-include 'object.py.jinja2'-%}
20 | {%-elif schema.type == 'null'-%}
21 | {%-include 'null.py.jinja2'-%}
22 | {%-elif schema.type == 'string'-%}
23 | {%-include 'primative.py.jinja2'-%}
24 | {%-else-%}
25 | # FIXME. Unable to generate a schema with type '{{schema.type}}'
26 | {%-endif-%}
27 | {%-endmacro-%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/string.md.jinja2:
--------------------------------------------------------------------------------
1 | **string**{%if 'title' in schema%} - {{schema.title|italics}}{%endif%}
2 | {%-if 'const' in schema%}
3 | * Value must be `{{schema.const}}`
4 | {%-elif 'enum' in schema%}
5 | * Value must be one of: {%for value in schema.enum%}`{{value}}` {%endfor%}
6 | {%-endif-%}
7 | {%-if 'const' not in schema and 'default' in schema%}
8 | * Default value is `{{schema.default}}`
9 | {%-endif-%}
10 | {%-if 'minLength' in schema and 'maxLength' in schema%}
11 | * Length between {{schema.minLength}}-{{schema.maxLength}}
12 | {%-elif 'minLength' in schema%}
13 | * Length longer than {{schema.minLength}}
14 | {%-elif 'maxLength' in schema%}
15 | * Length shorter than {{schema.maxLength}}
16 | {%-endif-%}
17 | {%-if 'pattern' in schema%}
18 | * Meets this regular expression: `{{schema.pattern}}`
19 | {%-endif%}
20 | {%-if 'format' in schema-%}
21 | {%-if schema.format == 'uuid'%}
22 | * Must be a **UUID**
23 | {%-elif schema.format == 'date-time'%}
24 | * Must be a ISO 8601 Timestamp
25 | {%-else%}
26 | * Format is `{{schema.format}}`
27 | {%-endif%}
28 | {%-endif%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/header.hpp.jinja2:
--------------------------------------------------------------------------------
1 | // This file is automatically generated using a JSON-Schema definition.
2 | // DO NOT MODIFY this file, as changes will be overwritten on the next code
3 | // generation.
4 |
5 | #pragma once
6 |
7 | #include
8 | {%-for dep in deps|sort|reverse %}
9 | {%-if 'rapidjson' not in dep %}{# rapidjson should go last and always #}
10 | #include {{dep}}
11 | {%-endif-%}
12 | {%-endfor%}
13 | #include "{{resolver.cpp_get_lib_ns() | join("/")}}{%if resolver.cpp_get_lib_ns() %}/{%endif%}exceptions.hpp"
14 |
15 | // FIXME: Somehow rapidjson provides a copy of inttypes.h that conflicts
16 | // with what google/breakpad needs. This flag gets things to compile, but
17 | // we probably need to look into the issue further, especially if we
18 | // ever try to use 64-bit ints.
19 | #define RAPIDJSON_NO_INT64DEFINE
20 | #include "rapidjson/document.h"
21 |
22 | {%for n in ns %}
23 | namespace {{n}} {
24 | {%-endfor%}
25 |
26 | {%from 'loader.jinja2' import Class with context%}
27 | {{Class('hpp', resolver, ns, Name, schema)}}
28 |
29 | {%-for n in ns|reverse %}
30 | } // end ns {{n}}
31 | {%-endfor%}
32 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/integer.md.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro IntFormat(schema)-%}
2 | {%-if 'format' in schema-%}
3 | {%-if schema['format'].startswith('u')%}Unsigned {%endif-%}
4 | {%-if schema['format'].endswith('64')%}64-bit {%endif-%}
5 | {%-if schema['format'].endswith('32')%}32-bit {%endif-%}
6 | {%-if schema['format'].endswith('16')%}16-bit {%endif-%}
7 | {%-if schema['format'].endswith('8')%}8-bit {%endif-%}
8 | {%-endif-%}
9 | {%-endmacro-%}
10 | **{{IntFormat(schema)}}integer**{%if 'title' in schema%} - *{{schema.title}}*{%endif%}
11 | {%-if 'default' in schema%}
12 | * Default value: `{{schema.default}}`
13 | {%-endif-%}
14 | {%-if 'minimum' in schema%}
15 | * Greater than or equal to {{schema.minimum}}
16 | {%-endif-%}
17 | {%if 'maximum' in schema%}
18 | * Less than or equal to {{schema.maximum}}
19 | {%-endif-%}
20 | {%if 'exclusiveMinimum' in schema%}
21 | * Greater than {{schema.exclusiveMinimum}}
22 | {%-endif-%}
23 | {%if 'exclusiveMaximum' in schema%}
24 | * Less than {{schema.maximum}}
25 | {%-endif-%}
26 | {%if 'multipleOf' in schema%}
27 | * Multiple of {{schema.multipleOf}}
28 | {%-endif-%}
29 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/markdown/array.md.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro numItems(schema)-%}
2 | {%-if schema.minItems is defined-%}
3 | {%-if schema.maxItems is defined-%}
4 | {%-if schema.minItems == schema.maxItems -%}
5 | There must be exactly {{schema.minItems}} elements.
6 | {%-else-%}
7 | There must be {{schema.minItems}} to {{schema.maxItems}} elements.
8 | {%-endif-%}
9 | {%-else-%}{#no maxitems#}
10 | There must be at least {{schema.minItems}} elements.
11 | {%-endif-%}
12 | {%-elif schema.maxItems is defined-%}
13 | There must be at most {{schema.maxItems}} elements.
14 | {%-endif-%}
15 | {%-endmacro-%}
16 | {%-import 'schema.jinja2' as loader-%}
17 | **Array**{%if 'title' in schema%} - {{schema.title|italics}}.{%endif%} {%if not schema.IsTupleSchema() %}{{numItems(schema)}} Each element is:
18 |
19 | {{loader.Display(schema.GetItemSchema().Resolve(resolver), resolver) | replace("\n", "\n> ")}}
20 | {%-else%} The array must have {{schema.NumberTupleItems()}} elements: {%for item in schema.GetTupleSchema()%}
21 | Element {{loop.index}}:
22 | {{loader.Display(item.Resolve(resolver), resolver) | replace("\n", "\n> ")}}
23 | {%endfor%}{%endif%}
24 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | with open("requirements.txt", "r") as fp:
7 | requirements = fp.read().split('\n')
8 |
9 | import jsonschemacodegen._version as _version
10 |
11 | setup(name='json-schema-codegen',
12 | version=_version.__version__,
13 | url='http://github.com/pearmaster/json-schema-codegen',
14 | author='Jacob Brunson',
15 | author_email='pypi@jacobbrunson.com',
16 | description="Generate C++ or Python structures from JSON-Schema",
17 | long_description=long_description,
18 | long_description_content_type="text/markdown",
19 | license='GPLv2',
20 | packages=[
21 | 'jsonschemacodegen',
22 | 'jsonschemacodegen.templates.cpp',
23 | 'jsonschemacodegen.templates.python',
24 | 'jsonschemacodegen.templates.markdown',
25 | ],
26 | package_data={
27 | 'jsonschemacodegen.templates.cpp': ['*.jinja2'],
28 | 'jsonschemacodegen.templates.python': ['*.jinja2'],
29 | 'jsonschemacodegen.templates.markdown': ['*.jinja2'],
30 | },
31 | zip_safe=False,
32 | install_requires=requirements,
33 | include_package_data=True,
34 | python_requires='>=3.7',
35 | )
--------------------------------------------------------------------------------
/examples/example_markdown.py:
--------------------------------------------------------------------------------
1 | from jsonschemacodegen import templator
2 | from jsonschemacodegen.resolver import SimpleResolver
3 | from jsonschemacodegen.schemawrappers import SchemaFactory
4 |
5 | schema = {
6 | "type" : "object",
7 | "properties": {
8 | "oneOfSomething": {
9 | 'oneOf': [
10 | {
11 | 'type': 'integer',
12 | 'minimum': 0,
13 | 'multipleOf': 3,
14 | },
15 | {
16 | "type": "string"
17 | },
18 | {
19 | "type": "null"
20 | },
21 | ]
22 | },
23 | "myName": {
24 | "type": "string",
25 | "minLength": 1
26 | },
27 | "anObject": {
28 | "type": "object",
29 | "properties": {
30 | "one": {
31 | "type": "string",
32 | "pattern": "[A-Z]*",
33 | },
34 | "two": {
35 | "type": "number",
36 | },
37 | }
38 | }
39 | },
40 | "required": [
41 | "myName"
42 | ]
43 | }
44 |
45 |
46 |
47 | if __name__ == '__main__':
48 | simpleResolver = SimpleResolver("myproject")
49 | t = templator.Generator('jsonschemacodegen.templates.markdown', 'output')
50 | schema = SchemaFactory(schema)
51 | t.RenderTemplate('description.md.jinja2', 'example.md', schema=schema, resolver=simpleResolver)
52 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/bool.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set std = resolver.cpp_resolve_namespace(['std']) %}
2 | {%-set rapidjson = resolver.cpp_resolve_namespace(['rapidjson']) %}
3 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
4 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
5 | {{className}}::{{Name}}(bool value)
6 | {
7 | Set(value);
8 | }
9 |
10 | {%-if schema.default is defined %}
11 | {%set emptyConstructor = true %}
12 | {{className}}::{{Name}}()
13 | {
14 | Set({{schema.default|lower}});
15 | }
16 | {%-endif%}
17 |
18 | {{className}}::operator bool() const
19 | {
20 | return Get();
21 | }
22 |
23 | {{className}}& {{className}}::operator=(bool value)
24 | {
25 | Set(value);
26 | return *this;
27 | }
28 |
29 | void {{className}}::Set(bool value)
30 | {
31 | _value = value;
32 | }
33 |
34 | bool {{className}}::Get() const
35 | {
36 | return _value;
37 | }
38 |
39 | {{className}} {{className}}::FromJson(const {{rapidjson}}Value& json)
40 | {
41 | if (!(json.IsBool()))
42 | {
43 | throw {{exception}}("Not a boolean");
44 | }
45 |
46 | {{className}} newObject(json.GetBool());
47 | return newObject;
48 | }
49 |
50 | void {{className}}::ToJson({{rapidjson}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
51 | {
52 | value.SetBool(_value);
53 | }
54 |
55 | void {{className}}::SetHandle(const std::string& handle)
56 | {
57 | _handle = handle;
58 | }
59 |
60 | std::string {{className}}::GetHandle() const
61 | {
62 | return _handle;
63 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/loader.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro Class(filetype, resolver, ns, Name, schema) %}
2 | {%-if schema.oneOf is defined -%}
3 | {%-include 'oneof.'+filetype+'.jinja2'-%}
4 | {%-elif schema.allOf is defined -%}
5 | {%-include 'allof.'+filetype+'.jinja2'-%}
6 | {%-elif schema.anyOf is defined -%}
7 | {%-include 'anyof.'+filetype+'.jinja2'-%}
8 | {%-elif '$ref' in schema -%}
9 | // An object representing {{schema['$ref']}} is used in this structure
10 | {# -#}
11 | {%-if filetype == 'hpp' -%}
12 | {%-include 'ref.hpp.jinja2'-%}
13 | {%-endif-%}
14 | {%-elif 'type' not in schema-%}
15 | {%-elif schema.type == 'integer' or schema.type == 'number'-%}
16 | {%-include 'numeric.'+filetype+'.jinja2'-%}
17 | {%-elif schema.type == 'array'-%}
18 | {%-include 'array.'+filetype+'.jinja2'-%}
19 | {%-elif schema.type == 'object'-%}
20 | {%-include 'object.'+filetype+'.jinja2'-%}
21 | {%-elif schema.type == 'boolean'-%}
22 | {%-include 'bool.'+filetype+'.jinja2'-%}
23 | {%-elif schema.type == 'null'-%}
24 | {%-include 'null.'+filetype+'.jinja2'-%}
25 | {%-elif schema.type == 'string' and schema.enum is not defined-%}
26 | {%-if schema.format == 'date-time'-%}
27 | {%-include 'datetime.'+filetype+'.jinja2'-%}
28 | {%-else-%}
29 | {%-include 'string.'+filetype+'.jinja2'-%}
30 | {%-endif-%}
31 | {%-elif schema.type == 'string' and schema.enum is defined-%}
32 | {%-include 'string_enum.'+filetype+'.jinja2'-%}
33 | {%-else-%}
34 | // FIXME: Failed at attempt to generate object for schema of type {{schema.type-}}
35 | {%-endif-%}
36 | {%-endmacro-%}
37 | {%-macro Reference(resolver, ref) %}
38 | {{-resolver.cpp_get_ns_name(ref)-}}
39 | {%-endmacro-%}
40 |
--------------------------------------------------------------------------------
/examples/example_json.py:
--------------------------------------------------------------------------------
1 | from jsonschemacodegen.json_example import GeneratorFromSchema
2 | from jsonschemacodegen import schemawrappers
3 |
4 | if __name__ == '__main__':
5 | schema_structure = {
6 | "type": "object",
7 | "title": "outer structure",
8 | "properties": {
9 | "outerstr": {"type":"string", "example":"example outer string", "title":"outerstr"},
10 | "outerbool": {"type":"boolean", "examples": [False,True], "title":"outerbool"},
11 | "something": {
12 | "title": "somethingoneof",
13 | "oneOf": [
14 | {"type": "integer", "examples":[12,2,3], "title": "oneof something is integer with three examples"},
15 | {
16 | "type": "object",
17 | "title": "oneof something is object with two properties",
18 | "properties": {
19 | "innerone": {
20 | "title": "innerone property is oneof",
21 | "oneOf": [
22 | {"type": "integer", "examples":[4,5], "title":"oneof innerone is integer with 2 examples"},
23 | {"type": "boolean", "title": "oneof innerone is boolean"}
24 | ]
25 | },
26 | "innertwo": {"type": "string", "example": "inner two", "title":"innertwo"}
27 | }
28 | }
29 | ]
30 | }
31 | },
32 | "required": [
33 | "outerbool"
34 | ]
35 | }
36 | schema = schemawrappers.SchemaFactory(schema_structure)
37 | generator = GeneratorFromSchema(None)
38 | examples = generator.GenerateSome(schema, number_of_examples=10)
39 | print(f"{len(examples)} number of examples:")
40 | for ex in examples:
41 | print(ex)
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/null.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
2 | /*! {{Name}} is a wrapper around "null".
3 | {%-if schema.description %}
4 | * {{schema.description}}
5 | {%-endif%}
6 | */
7 | class {{Name}}
8 | {
9 | public:
10 | /*! Constructor
11 | */
12 | {{Name}}();
13 |
14 | virtual ~{{Name}}() = default;
15 |
16 | /*! \fn boost::none_t Get() const
17 | * \brief returns boost::none which represents "null"
18 | * This is pointless because it never returns anything other than boost::none
19 | * \returns boost::none
20 | */
21 | boost::none_t Get() const;
22 |
23 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
24 | * \brief Deserializes a JSON "null" value into a new instance of {{Name}}.
25 | * \param json is the RapidJSON value which must be of "null" type.
26 | * \throw {{exception}} If the JSON isn't a null
27 | * \returns {{Name}}
28 | */
29 | static {{Name}} FromJson(const rapidjson::Value& json);
30 |
31 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
32 | * \brief Sets 'value' to null
33 | * \param value is the RapidJSON value which will be modified to contain the serialization
34 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
35 | */
36 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
37 |
38 | /*! Sets a string handle associated with this {{Name}} instance.
39 | * This gets called by a parent object after creating an instance that is used for an object's property.
40 | * \param handle is the string name.
41 | */
42 | void SetHandle(const std::string& handle);
43 |
44 | /*! Gets the string handle associated with this {{Name}} instance.
45 | * This is often the property name used in a JSON-object parent.
46 | * It may be empty.
47 | * \returns the handle string
48 | */
49 | std::string GetHandle() const;
50 | private:
51 | std::string _handle;
52 | };
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/array.py.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader-%}
2 |
3 | {%-if 'items' in schema-%}
4 | {%-if '$ref' in schema['items']-%}
5 | {%-set itemType = resolver.py_class_name(schema['items']['$ref'])-%}
6 | {%-endif-%}
7 | {%-endif-%}
8 |
9 | {%-set fullClassPath -%}
10 | {%-if parentClass is not defined or parentClass is none-%}
11 | {{Name}}
12 | {%-else-%}
13 | {{parentClass}}.{{Name}}
14 | {%-endif-%}
15 | {%-endset-%}
16 |
17 | class {{Name}} (UserList):
18 | """ This represents a JSON array.
19 | """
20 | {%-if 'items' in schema%}
21 | {%-if '$ref' not in schema['items']%}
22 | {%-set itemType = 'self.Item'%}
23 | {{loader.Class(resolver, 'Item', schema['items'], fullClassPath) |indent(4)}}
24 | {%-endif%}
25 | {%-endif%}
26 |
27 | def __init__(self, the_list=None):
28 | """Initializer for array.
29 | """
30 | if not hasattr(the_list, '__iter__'):
31 | raise TypeError("The provided list was not iterable")
32 |
33 | self.the_list = the_list
34 |
35 | if isinstance(the_list, type(self)):
36 | super().__init__(the_list.data)
37 | else:
38 | {%-if schema.minItems is defined%}
39 | if len(the_list) < {{schema.minItems}}:
40 | raise ValueError("Provided array is too short")
41 | {%-endif%}
42 | {%-if schema.maxItems is defined%}
43 | if len(the_list) > {{schema.maxItems}}:
44 | raise ValueError("Provided array is too long")
45 | {%-endif%}
46 | super().__init__([{{itemType}}(x) for x in the_list])
47 |
48 | def Append(self, new_value) -> {{Name}}:
49 | {%-if schema.maxItems is defined%}
50 | if (len(self.the_list)+1) > {{schema.maxItems}}:
51 | raise ValueError("Appending would make the array too long")
52 | {%-endif%}
53 | self.data.append({{itemType}}(new_value))
54 | return self
55 |
56 | def Serializable(self) -> list:
57 | return self.data
58 |
59 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/object.py.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader-%}
2 |
3 | {%-macro PropertyClassName(propName)-%}
4 | {{propName|PascalCase}}Property
5 | {%-endmacro-%}
6 |
7 | {%-macro PropertyType(propName, propSchema)-%}
8 | {%-if '$ref' in propSchema-%}
9 | {{resolver.py_class_name(propSchema['$ref'])}}
10 | {%-else-%}
11 | self.{{PropertyClassName(propName)}}
12 | {%-endif-%}
13 | {%-endmacro-%}
14 |
15 | {%-set fullClassPath -%}
16 | {%-if parentClass is not defined or parentClass is none-%}
17 | {{Name}}
18 | {%-else-%}
19 | {{parentClass}}.{{Name}}
20 | {%-endif-%}
21 | {%-endset-%}
22 |
23 | class {{Name}}(UserDict):
24 | """This represents a JSON object.
25 | """
26 |
27 | {%-for propName, propSchema in schema.properties.items()%}
28 | {%-if '$ref' not in prop%}
29 | {{loader.Class(resolver, PropertyClassName(propName), propSchema, fullClassPath) |indent(4)}}
30 | {%-endif%}
31 | {%-endfor%}
32 |
33 | def __init__(self, data=None, **kwargs):
34 | """Initialization for the {{Name}} object.
35 | It can be initialized with an object, or by passing each
36 | object property as a keyword argument.
37 | """
38 | new_data = {}
39 | {%-for propName, propSchema in schema.properties.items()%}
40 | try:
41 | prop = data["{{propName}}"] if ("{{propName}}" in data) else kwargs["{{propName}}"]
42 | if not isinstance(prop, {{PropertyType(propName, propSchema)}}):
43 | new_data["{{propName}}"] = {{PropertyType(propName, propSchema)}}(prop)
44 | except KeyError:
45 | {%-if propName in schema.required%}
46 | raise ValueError("Missing property '{{propName}}'")
47 | {%-else%}
48 | pass
49 | {%-endif%}
50 | {%-endfor%}
51 | super().__init__(new_data)
52 |
53 | {%-for propName, propSchema in schema.properties.items()%}
54 |
55 | def Get{{propName|PascalCase}}(self):
56 | return self.data["{{propName}}"]
57 |
58 | def Set{{propName|PascalCase}}(self, new_value) -> {{fullClassPath}}:
59 | if not isinstance(new_value, {{PropertyType(propName, propSchema)}}):
60 | self.data["{{propName}}"] = {{PropertyType(propName, propSchema)}}(new_value)
61 | else:
62 | self.data["{{propName}}"] = new_value
63 | return self
64 | {%-endfor%}
65 |
66 | def Serializable(self) -> dict:
67 | return self.data
68 | {# #}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/bool.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
2 | /*! {{Name}} is a wrapper around a boolean.
3 | {%-if schema.description %}
4 | * {{schema.description}}
5 | {%-endif%}
6 | */
7 | class {{Name}}
8 | {
9 | public:
10 | /*! \fn {{Name}}(bool value)
11 | * \brief Constructor with initial value
12 | * \param value initial value
13 | */
14 | {{Name}}(bool value);
15 |
16 | {%-if schema.default is defined %}
17 | /*! \fn {{Name}}()
18 | * \brief Constructor that uses default value of {{schema.default}}
19 | */
20 | {{Name}}();
21 | {%-endif%}
22 |
23 | virtual ~{{Name}}() = default;
24 |
25 | /*! Cast to bool operator
26 | */
27 | operator bool() const;
28 |
29 | /*! Assign from bool operator
30 | */
31 | {{Name}}& operator=(bool value);
32 |
33 | /*! \fn void Set(bool value)
34 | * \brief reset value to new bool
35 | * \param value new value
36 | */
37 | void Set(bool value);
38 |
39 | /*! \fn bool Get() const
40 | * \brief get current boolean value
41 | */
42 | bool Get() const;
43 |
44 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
45 | * \brief Deserializes JSON into a new instance of the {{Name}} object.
46 | * \param json is the RapidJSON value which must be a boolean type.
47 | * \throw {{exception}} If the JSON data is not a boolean
48 | * \returns {{Name}}
49 | */
50 | static {{Name}} FromJson(const rapidjson::Value& json);
51 |
52 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
53 | * \brief Serializes boolean value to JSON
54 | * \param value is the RapidJSON value which will be modified to contain the serialization
55 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
56 | */
57 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
58 |
59 | /*! Sets a string handle associated with this {{Name}} instance.
60 | * This gets called by a parent object after creating an instance that is used for an object's property.
61 | * \param handle is the string name.
62 | */
63 | void SetHandle(const std::string& handle);
64 |
65 | /*! Gets the string handle associated with this {{Name}} instance.
66 | * This is often the property name used in a JSON-object parent.
67 | * It may be empty.
68 | * \returns the handle string
69 | */
70 | std::string GetHandle() const;
71 | private:
72 | bool _value;
73 | std::string _handle;
74 | };
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/oneof.py.jinja2:
--------------------------------------------------------------------------------
1 |
2 | {%-import 'loader.jinja2' as loader-%}
3 |
4 | {%-macro ComponentName(schema, index)-%}
5 | {%-if 'title' in schema-%}
6 | {{schema.title|PascalCase}}Option
7 | {%-else-%}
8 | Option{{index}}
9 | {%-endif-%}
10 | {%-endmacro-%}
11 |
12 | {%-macro ComponentType(schema, index)-%}
13 | {%-if '$ref' in schema-%}
14 | {{resolver.py_class_name(schema['$ref'])}}
15 | {%-else-%}
16 | self.{{ComponentName(schema, index)}}
17 | {%-endif-%}
18 | {%-endmacro-%}
19 |
20 | {%-set fullClassPath -%}
21 | {%-if parentClass is not defined or parentClass is none-%}
22 | {{Name}}
23 | {%-else-%}
24 | {{parentClass}}.{{Name}}
25 | {%-endif-%}
26 | {%-endset-%}
27 |
28 | class {{Name}}(object):
29 | """This represents one of {{schema['oneOf'] | length}} options"
30 | {%-for comp in schema['oneOf']%}
31 | * {{ComponentName(comp, loop.index)}}
32 | {%-endfor%}
33 | """
34 | {%-for comp in schema['oneOf']%}
35 | {%-if '$ref' not in comp%}
36 | {{loader.Class(resolver, ComponentName(comp, loop.index), comp, fullClassPath) |indent(4)}}
37 | {%-endif%}
38 | {%-endfor%}
39 |
40 | def __init__(self, initial):
41 | """Constructor. Some initial data for one of the options must be passed.
42 | """
43 | self.Set(initial)
44 |
45 | def Get(self):
46 | """Gets one of the supported option objects.
47 | """
48 | return self._value
49 |
50 | def Set(self, data) -> {{fullClassPath}}:
51 | """ Must pass an object or data for one of the supported
52 | option types.
53 | """
54 | if isinstance(data, type(self)):
55 | self._value = data._value
56 | self._type = data._type
57 | else:
58 | self._type = None
59 | self._value = None
60 | {%-for compSchema in schema['oneOf']%}
61 | if self._type is None:
62 | try:
63 | self._value = {{ComponentType(compSchema, loop.index)}}(data)
64 | except (ValueError, TypeError):
65 | pass
66 | else:
67 | self._type = {{ComponentType(compSchema, loop.index)}}
68 | {%-endfor%}
69 | if self._type is None:
70 | raise ValueError("Provided data did not match one of the required formats")
71 | return self
72 |
73 | def Serializable(self):
74 | """ Returns an object that can be JSON serialized.
75 | For example, use: `json.dumps(thisObject, default=lambda a: a.Serializable())`
76 | """
77 | return self._value.Serializable()
78 |
79 |
--------------------------------------------------------------------------------
/jsonschemacodegen/json_example.py:
--------------------------------------------------------------------------------
1 | import json
2 | import random
3 | import abc
4 | from copy import copy
5 | from . import schemawrappers
6 |
7 | class SchemaResolverBaseClass(abc.ABC):
8 |
9 | @abc.abstractmethod
10 | def get_schema(self, reference, root=None):
11 | """Given a reference, returns a wrapped schema object.
12 | """
13 | pass
14 |
15 | @abc.abstractmethod
16 | def get_json(self, reference, root=None) -> dict:
17 | pass
18 |
19 | @abc.abstractmethod
20 | def get_document(self, reference):
21 | pass
22 |
23 |
24 | class GeneratorFromSchema(object):
25 |
26 | def __init__(self, resolver=None):
27 | self.resolver = resolver
28 |
29 | @staticmethod
30 | def DeDuplicate(aList : list, limit=None) -> list:
31 | text_list = [json.dumps(a, sort_keys=True) for a in aList]
32 | text_unique = []
33 | for t in text_list:
34 | if len(text_unique) == limit:
35 | break
36 | if t not in text_unique:
37 | text_unique.append(t)
38 | text_sorted = sorted(text_unique, key=len)
39 | return [json.loads(s) for s in text_sorted]
40 |
41 | def GenerateSome(self, schema, number_of_examples=2, random_seed=0xBEEF) -> list:
42 | examples = []
43 | indexes = []
44 | random.seed(random_seed)
45 | number_of_combos = schema.GetExampleCombos(self.resolver)
46 | bits_for_combos = schemawrappers.bitsNeededForNumber(number_of_combos)
47 | index_max = 1 << bits_for_combos
48 | if number_of_examples >= index_max:
49 | indexes = [schemawrappers.ExampleIndex(i) for i in range(0, index_max)]
50 | elif (number_of_examples*3) >= index_max:
51 | indexes = [schemawrappers.ExampleIndex(i) for i in range(1, index_max)]
52 | random.shuffle(indexes)
53 | else:
54 | index_numbers = []
55 | while len(indexes) < number_of_examples*3:
56 | rand_index = random.randint(1, index_max)
57 | if rand_index not in index_numbers:
58 | indexes.append(schemawrappers.ExampleIndex(rand_index))
59 | index_numbers.append(rand_index)
60 | for index in indexes:
61 | ex = schema.Example(self.resolver, index)
62 | examples.append(ex)
63 | return self.DeDuplicate(examples, limit=number_of_examples)
64 |
65 | def GenerateFull(self, schema) -> list:
66 | index = schemawrappers.ExampleIndex(-1)
67 | return [schema.Example(self.resolver, index)]
68 |
69 | def GenerateLimited(self, schema) -> list:
70 | index = schemawrappers.ExampleIndex(0)
71 | return [schema.Example(self.resolver, index)]
72 |
73 | def Generate(self, schema, number_of_examples=3) -> list:
74 | examples = []
75 | if number_of_examples > 0:
76 | ex = self.GenerateFull(schema)
77 | examples.extend(ex)
78 | if number_of_examples > 1:
79 | ex = self.GenerateLimited(schema)
80 | examples.extend(ex)
81 | if number_of_examples > 2:
82 | examples.extend(self.GenerateSome(schema, number_of_examples))
83 | ret = self.DeDuplicate(examples, limit=number_of_examples)
84 | return ret
85 |
86 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/import.py.jinja2:
--------------------------------------------------------------------------------
1 | {%-macro Combination(schema)-%}
2 | {%-if 'allOf' in schema-%}
3 | allOf
4 | {%-elif 'anyOf' in schema-%}
5 | anyOf
6 | {%-endif-%}
7 | {%-endmacro-%}
8 |
9 | {%-macro GetImports(resolver, schema, includes)-%}
10 | {%-do includes.append('from __future__ import annotations')-%}
11 | {%-if schema.oneOf is defined-%}
12 | {%- do includes.append('from collections import UserDict')-%}
13 | {%- for comp in schema['oneOf']-%}
14 | {%- if '$ref' in comp-%}
15 | {%- do includes.append(resolver.py_include_statement(comp['$ref']))-%}
16 | {%- else-%}
17 | {{ GetImports(resolver, comp, includes)}}
18 | {%- endif-%}
19 | {%- endfor-%}
20 | {%-elif schema.allOf is defined-%}
21 | {%- for comp in schema[Combination(schema)]-%}
22 | {%- if '$ref' in comp-%}
23 | {%- do includes.append(resolver.py_include_statement(comp['$ref']))-%}
24 | {%- else-%}
25 | {{ GetImports(resolver, comp, includes)}}
26 | {%- endif-%}
27 | {%- endfor-%}
28 | {%-elif schema.anyOf is defined-%}
29 | {%- do includes.append('from collections import UserDict')-%}
30 | {%- for comp in schema[Combination(schema)]-%}
31 | {%- if '$ref' in comp-%}
32 | {%- do includes.append(resolver.py_include_statement(comp['$ref']))-%}
33 | {%- endif-%}
34 | {%- endfor-%}
35 | {%- for comp in schema[Combination(schema)]-%}
36 | {%- if '$ref' not in comp-%}
37 | {{ GetImports(resolver, comp, includes)}}
38 | {%- endif-%}
39 | {%- endfor-%}
40 | {%-elif '$ref' in schema-%}
41 | {# This object makes use of an external schema specified at {{schema['$ref']}}#}
42 | {%-elif 'type' not in schema-%}
43 | {%-elif schema.type == 'integer'
44 | or schema.type == 'number'
45 | or schema.type == 'boolean'
46 | or schema.type == 'string'-%}
47 | {%- if schema.pattern is defined-%}
48 | {%- do includes.append('import re')-%}
49 | {%- endif-%}
50 | {%-elif schema.type == 'array'-%}
51 | {%- do includes.append('from collections import UserList')-%}
52 | {%- if 'items' in schema-%}
53 | {%- if '$ref' in schema['items']-%}
54 | {%- do includes.append(resolver.py_include_statement(schema['items']['$ref']))-%}
55 | {%- endif-%}
56 | {%- endif-%}
57 | {%- if 'items' in schema-%}
58 | {%- if '$ref' not in schema['items']-%}
59 | {%- set itemType = 'self.Item'-%}
60 | {{ GetImports(resolver, schema['items'], includes)}}
61 | {%- endif-%}
62 | {%- endif-%}
63 | {%-elif schema.type == 'object'-%}
64 | {%- do includes.append('from collections import UserDict')-%}
65 | {%- for prop in schema.properties.values()-%}
66 | {%- if '$ref' in prop-%}
67 | {%- do includes.append(resolver.py_include_statement(prop['$ref']))-%}
68 | {%- endif%}
69 | {%- endfor%}
70 | {%- for propName, propSchema in schema.properties.items()-%}
71 | {%- if '$ref' not in prop-%}
72 | {{ GetImports(resolver, propSchema, includes)}}
73 | {%- endif-%}
74 | {%- endfor%}
75 | {%-elif schema.type == 'null'-%}
76 | {# Nothing special here #}
77 | {%-elif schema.type == 'string'-%}
78 | {%- if schema.pattern is defined-%}
79 | {%- do includes.append('import re')-%}
80 | {%- endif-%}
81 | {%-else-%}
82 | {%-endif-%}
83 | {%-endmacro-%}
--------------------------------------------------------------------------------
/examples/example_asyncapi.py:
--------------------------------------------------------------------------------
1 | import jsonschemacodegen.cpp as cpp
2 | import jsonschemacodegen.resolver
3 | import sys
4 | import yaml
5 | import stringcase
6 | from pprint import pprint
7 |
8 | # This comes from the AsyncAPI Streetlights example
9 | # Only the 'components' section is shown because the
10 | # rest would be ignored.
11 | EXAMPLE_YAML="""
12 | components:
13 | messages:
14 | lightMeasured:
15 | name: lightMeasured
16 | title: Light measured
17 | summary: Inform about environmental lighting conditions for a particular streetlight.
18 | contentType: application/json
19 | traits:
20 | - $ref: '#/components/messageTraits/commonHeaders'
21 | payload:
22 | $ref: "#/components/schemas/lightMeasuredPayload"
23 | turnOnOff:
24 | name: turnOnOff
25 | title: Turn on/off
26 | summary: Command a particular streetlight to turn the lights on or off.
27 | traits:
28 | - $ref: '#/components/messageTraits/commonHeaders'
29 | payload:
30 | $ref: "#/components/schemas/turnOnOffPayload"
31 | dimLight:
32 | name: dimLight
33 | title: Dim light
34 | summary: Command a particular streetlight to dim the lights.
35 | traits:
36 | - $ref: '#/components/messageTraits/commonHeaders'
37 | payload:
38 | $ref: "#/components/schemas/dimLightPayload"
39 |
40 | schemas:
41 | lightMeasuredPayload:
42 | type: object
43 | properties:
44 | lumens:
45 | type: integer
46 | minimum: 0
47 | description: Light intensity measured in lumens.
48 | sentAt:
49 | $ref: "#/components/schemas/sentAt"
50 | turnOnOffPayload:
51 | type: object
52 | properties:
53 | command:
54 | type: string
55 | enum:
56 | - "on"
57 | - "off"
58 | description: Whether to turn on or off the light.
59 | sentAt:
60 | $ref: "#/components/schemas/sentAt"
61 | dimLightPayload:
62 | type: object
63 | properties:
64 | percentage:
65 | type: integer
66 | description: Percentage to which the light should be dimmed to.
67 | minimum: 0
68 | maximum: 100
69 | sentAt:
70 | $ref: "#/components/schemas/sentAt"
71 | sentAt:
72 | type: string
73 | format: date-time
74 | description: Date and time when the message was sent.
75 | """
76 |
77 | if __name__ == '__main__':
78 |
79 | spec = yaml.load(EXAMPLE_YAML)
80 |
81 | name = 'example'
82 |
83 | resolver = jsonschemacodegen.resolver.SimpleResolver('example')
84 | resolver.cpp_add_using(["asyncapi", name])
85 | resolver.cpp_set_root_namespace(["asyncapi"])
86 | output_dir = "output"
87 |
88 | generator = cpp.GeneratorFromSchema(src_output_dir=output_dir,
89 | header_output_dir=output_dir,
90 | resolver=resolver)
91 |
92 | for msgName, msg in spec['components']['messages'].items():
93 | path = "{}#/components/messages/{}".format(name, msgName)
94 | generator.Generate(msg['payload'], path)
95 |
96 | for schemaName, schema in spec['components']['schemas'].items():
97 | path = "{}#/components/schemas/{}".format(name, schemaName)
98 | generator.Generate(schema, path)
99 |
100 |
--------------------------------------------------------------------------------
/examples/example_python.py:
--------------------------------------------------------------------------------
1 | from jsonschemacodegen import python as pygen
2 | import json
3 |
4 | if __name__ == '__main__':
5 | schema = {
6 | "type": "array",
7 | "items": {
8 | "oneOf": [
9 | {
10 | "type": "string",
11 | "title": "StringOption",
12 | },
13 | {
14 | "type": "integer",
15 | "title": "InterGerOption",
16 | },
17 | {
18 | "type": "object",
19 | "title": "an object",
20 | "properties": {
21 | "astring": {"type": "string"},
22 | "aboolean": {"type": "boolean"},
23 | },
24 | "required": [
25 | "aboolean"
26 | ]
27 | },
28 | {
29 | "allOf": [
30 | {
31 | "type": "object",
32 | "properties": {
33 | "banana": {"type": "string"},
34 | },
35 | "required": ["banana"],
36 | },
37 | {
38 | "type": "object",
39 | "properties": {
40 | "apple": {"type": "string"},
41 | },
42 | "required": ["apple"]
43 | }
44 | ]
45 | },
46 | {
47 | "type": "null"
48 | }
49 | ]
50 | }
51 | }
52 | if True:
53 | generator = pygen.GeneratorFromSchema('output')
54 | generator.Generate(schema, 'Example', 'example')
55 |
56 | with open('output/__init__.py', 'w') as fp:
57 | pass
58 |
59 | from output import example
60 |
61 | def JsonPrint(o):
62 | print(json.dumps(o, default=lambda x: x.Serializable()))
63 |
64 | data = [
65 | "a",
66 | 1,
67 | {"astring": "bool is true", "aboolean": True},
68 | {"astring": "bool is false", "aboolean": False},
69 | {"banana": "yello", "apple": "red"},
70 | None,
71 | ]
72 | #exampleObj = example.Example(data)
73 | #assert(exampleObj[3].Get().GetAstring().Get() == data[3]['astring'])
74 | #assert(exampleObj[3].Get().GetAboolean().Get() == False)
75 |
76 | #JsonPrint(exampleObj)
77 |
78 | #f = example.Example()
79 | #JsonPrint(f)
80 |
81 | #nullObj1 = example.Example.Item.Option5()
82 | #nullObj2 = example.Example.Item.Option5(None)
83 | #nullObj3 = example.Example.Item.Option5(nullObj1)
84 | #g = example.Example.Item(nullObj3)
85 | #JsonPrint(g)
86 |
87 | #comboObject1 = example.Example.Item.Option4({"apple": "a", "banana": "b"})
88 | #comboObject2 = example.Example.Item.Option4(comboObject1)
89 | #comboObject3 = example.Example.Item.Option4({"apple":"ap"}, {"banana", "ba"})
90 | #comboObject4Comp1 = example.Example.Item.Option4.Component1(apple="ap")
91 | #comboObject3 = example.Example.Item.Option4(comboObject4Comp1, {"banana", "ba"})
92 | #h = example.Example.Item(comboObject2)
93 | #JsonPrint(h)
94 | JsonPrint(example.Example.Item({"banana": "yello", "apple": "red"}))
95 |
96 | #objectThree1 = example.Example.Item.AnObjectOption(astring="jacob", aboolean=True)
97 | #objectThree2 = example.Example.Item.AnObjectOption({"astring":"jacob", "aboolean":True})
98 | #objectThree3 = example.Example.Item.AnObjectOption(objectThree1)
99 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/datetime.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
2 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
3 | {%-set std = resolver.cpp_resolve_namespace(['std']) %}
4 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
5 | {{className}}::{{Name}}(const {{std}}string& value)
6 | {
7 | Set(value);
8 | }
9 |
10 | {{className}}::{{Name}}(const char* value)
11 | {
12 | Set(value);
13 | }
14 |
15 | {{className}}::{{Name}}()
16 | {
17 | {%-if schema.default is defined %}
18 | Set("{{schema.default}}");
19 | {%-endif%}
20 | }
21 |
22 | {{className}}::{{Name}}(const {{Name}}& other)
23 | {
24 | _value = other._value;
25 | }
26 |
27 | {{className}}::operator {{std}}string() const
28 | {
29 | return GetString();
30 | }
31 |
32 | {{className}}& {{className}}::operator=(const {{std}}string& value)
33 | {
34 | Set(value);
35 | return *this;
36 | }
37 |
38 | {{className}}& {{className}}::operator=(const char* value)
39 | {
40 | Set(value);
41 | return *this;
42 | }
43 |
44 | {{className}}& {{className}}::operator=(const boost::posix_time::ptime& value)
45 | {
46 | Set(value);
47 | return *this;
48 | }
49 | {%for origNs in originalNamespace %}
50 | namespace {{origNs}} {
51 | {%-endfor%}
52 | {{std}}ostream& operator<<({{std}}ostream& os, const {{className}}& obj)
53 | {
54 | os << obj.GetString();
55 | return os;
56 | }
57 | {%for origNs in originalNamespace %}}{%-endfor%} // end namespaces
58 |
59 | void {{className}}::Set(const {{std}}string& value)
60 | {
61 | try
62 | {
63 | {{std}}string fixedValue = boost::replace_nth_copy(value, "T", 0, " ");
64 | Set(boost::posix_time::time_from_string(fixedValue));
65 | }
66 | catch (const std::exception& e)
67 | {
68 | throw {{exception}}({{std}}string("Could not parse timestamp: ") + e.what());
69 | }
70 | }
71 |
72 | void {{className}}::Set(const char* value)
73 | {
74 | Set(std::string(value));
75 | }
76 |
77 | void {{className}}::Set(const boost::posix_time::ptime& datetime)
78 | {
79 | _value = datetime;
80 | }
81 |
82 | void {{className}}::SetNow()
83 | {
84 | Set(boost::posix_time::microsec_clock::universal_time());
85 | }
86 |
87 | void {{className}}::SetCurrent{{schema.format | PascalCase}}()
88 | {
89 | _value = boost::none;
90 | }
91 |
92 | std::string {{className}}::GetString() const
93 | {
94 | return boost::posix_time::to_iso_extended_string(Get());
95 | }
96 |
97 | bool {{className}}::IsCurrentTime() const
98 | {
99 | return !(_value);
100 | }
101 |
102 | boost::posix_time::ptime {{className}}::Get() const
103 | {
104 | if (IsCurrentTime())
105 | {
106 | return boost::posix_time::microsec_clock::universal_time();
107 | }
108 | return *_value;
109 | }
110 |
111 | {{className}} {{className}}::FromJson(const {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& json)
112 | {
113 | if (!(json.IsString()))
114 | {
115 | throw {{exception}}("Not a string");
116 | }
117 |
118 | return {{className}}(json.GetString());
119 | }
120 |
121 | {{className}} {{className}}::FromString(const {{std}}string& str)
122 | {
123 | return {{className}}(str);
124 | }
125 |
126 | void {{className}}::ToJson({{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
127 | {
128 | std::string tempVal(GetString());
129 | value.SetString(tempVal.c_str(), tempVal.size(), allocator);
130 | }
131 |
132 | void {{className}}::SetHandle(const std::string& handle)
133 | {
134 | _handle = handle;
135 | }
136 |
137 | std::string {{className}}::GetHandle() const
138 | {
139 | return _handle;
140 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/string_enum.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
2 | {%-set std = resolver.cpp_resolve_namespace(['std']) %}
3 | {%-set enumType = className+"::Value"%}
4 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
5 | {{className}}::{{Name}}({{enumType}} value)
6 | {
7 | Set(value);
8 | }
9 |
10 | {%-if schema.default is defined %}
11 | {%set emptyConstructor = true %}
12 | {{className}}::{{Name}}()
13 | {
14 | Set(DEFAULT_VALUE);
15 | }
16 | {%-endif%}
17 |
18 | {{className}}::operator {{enumType}}() const
19 | {
20 | return Get();
21 | }
22 |
23 | {{className}}& {{className}}::operator=({{enumType}} value)
24 | {
25 | Set(value);
26 | return *this;
27 | }
28 | {%for origNs in originalNamespace %}
29 | namespace {{origNs}} {
30 | {%-endfor%}
31 | {{std}}ostream& operator<<({{std}}ostream& os, const {{className}}& v)
32 | {
33 | os << v.EnumToString(v._value);
34 | return os;
35 | }
36 |
37 | bool operator< (const {{className}}& left, const {{className}}& right)
38 | {
39 | return left._value < right._value;
40 | }
41 |
42 | {{std}}size_t hash_value(const {{className}}& e)
43 | {
44 | return boost::hash_value(e.ToString());
45 | }
46 | {%for origNs in originalNamespace %}}{%-endfor%} // end namespaces
47 |
48 | void {{className}}::Set({{enumType}} value)
49 | {
50 | _value = value;
51 | }
52 |
53 | {{enumType}} {{className}}::Get() const
54 | {
55 | return _value;
56 | }
57 |
58 | {{std}}string {{className}}::EnumToString({{enumType}} value)
59 | {
60 | switch (value)
61 | {
62 | {%-for enum in schema.enum%}
63 | case {{enumType}}::{{enum | enumify}}:
64 | return "{{enum}}";
65 | {%-endfor%}
66 | }
67 | // By not including a 'defaut' case in the switch statement, we should ensure that
68 | // all values of the enum are handled. However, some compilers like to see a return
69 | // statement when no cases are matched. While this never happens, throwing here
70 | // avoids a compiler warning.
71 | throw std::out_of_range("No valid string for invalid enum value");
72 | }
73 |
74 | {{enumType}} {{className}}::StringToEnum(const std::string& input)
75 | {
76 | {%-for enum in schema.enum %}
77 | {%if not loop.first%}else {%endif%}if (input == "{{enum}}")
78 | {
79 | return {{enumType}}::{{enum | enumify}};
80 | }
81 | {%-endfor%}
82 | else
83 | {
84 | throw std::out_of_range("Could not find enum value for string");
85 | }
86 | }
87 |
88 | {{className}} {{className}}::FromJson(const {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& json)
89 | {
90 | if (!(json.IsString()))
91 | {
92 | throw {{exception}}("JSON wasn't a string");
93 | }
94 |
95 | {{std}}string testValue = json.GetString();
96 | try
97 | {
98 | return {{className}}(StringToEnum(testValue));
99 | }
100 | catch (const std::exception& e)
101 | {
102 | throw {{exception}}(e);
103 | }
104 | }
105 |
106 | {{className}} {{className}}::FromString(const {{std}}string& str)
107 | {
108 | return {{className}}(StringToEnum(str));
109 | }
110 |
111 | void {{className}}::ToJson({{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
112 | {
113 | {{std}}string strValue = EnumToString(_value);
114 | value.SetString(strValue.c_str(), strValue.size(), allocator);
115 | }
116 |
117 | {{std}}string {{className}}::ToString() const
118 | {
119 | return EnumToString(_value);
120 | }
121 |
122 | void {{className}}::SetHandle(const std::string& handle)
123 | {
124 | _handle = handle;
125 | }
126 |
127 | std::string {{className}}::GetHandle() const
128 | {
129 | return _handle;
130 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/anyof.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
2 | {%macro NestedObjectName(name) -%}
3 | {{className}}::{{-name | UpperCamelCase-}}
4 | {%-endmacro%}
5 | {%-macro ObjectType(propName, propSchema) -%}
6 | {%-if '$ref' in propSchema -%}
7 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
8 | {%-else-%}
9 | {{-NestedObjectName(propName)-}}
10 | {%-endif-%}
11 | {%-endmacro%}
12 | {%-macro ObjectName(propName, propSchema) -%}
13 | {%-if '$ref' in propSchema -%}
14 | {{-resolver.cpp_get_name(propSchema['$ref'])|UpperCamelCase-}}
15 | {%-else-%}
16 | {{-propName | UpperCamelCase-}}
17 | {%-endif-%}
18 | {%-endmacro%}
19 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
20 |
21 | {%import 'loader.jinja2' as loader with context%}
22 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
23 | {{loader.Class('cpp', resolver, [className], componentName, s) }}
24 | {%-endfor%}
25 |
26 |
27 | {{className}}::{{Name}}()
28 | {
29 |
30 | }
31 |
32 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
33 | boost::optional<{{ObjectType(componentName, s)}}> {{className}}::Get{{ObjectName(componentName, s)}}() const
34 | {
35 | return {{componentName|privatize}};
36 | }
37 |
38 | void {{className}}::Set{{ObjectName(componentName, s)}}(const {{ObjectType(componentName, s)}}& component)
39 | {
40 | {{componentName|privatize}} = component;
41 | {{componentName|privatize}}->SetHandle(_handle);
42 | }
43 | {%endfor%}
44 |
45 | {{className}} {{className}}::FromJson(const rapidjson::Value& json)
46 | {
47 | {%-if schema.requiredProperties | length > 0 %}
48 | ThrowIfMissingRequiredProperties(json);
49 | {%-endif%}
50 | {{className}} returnObject;
51 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
52 | try
53 | {
54 | returnObject.Set{{ObjectName(componentName, s)}}({{ObjectType(componentName, s)}}::FromJson(json));
55 | }
56 | catch (...)
57 | {
58 | // If the type didn't parse, then no big deal since AnyOf doesn't require it to
59 | }
60 | {%-endfor%}
61 |
62 | return returnObject;
63 | }
64 |
65 | void {{className}}::ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const
66 | {
67 | if (!value.IsObject())
68 | {
69 | value.SetObject();
70 | }
71 |
72 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
73 | if ({{componentName|privatize}})
74 | {
75 | {{componentName|privatize}}->ToJson(value, allocator);
76 | }
77 | {%-endfor %}
78 | {%-if schema.requiredProperties | length > 0 %}
79 | ThrowIfMissingRequiredProperties(value);
80 | {%-endif%}
81 | }
82 |
83 | void {{className}}::SetHandle(const std::string& handle)
84 | {
85 | _handle = handle;
86 |
87 | // This 'anyOf' object passes the handle through to its children objects
88 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
89 | if ({{componentName|privatize}})
90 | {
91 | {{componentName|privatize}}->SetHandle(handle);
92 | }
93 | {%-endfor %}
94 | }
95 |
96 | std::string {{className}}::GetHandle() const
97 | {
98 | return _handle;
99 | }
100 |
101 | {%if schema.requiredProperties | length > 0 %}
102 | void {{className}}::ThrowIfMissingRequiredProperties(const rapidjson::Value& json)
103 | {
104 | if (!json.IsObject())
105 | {
106 | throw {{exception}}("Not an object");
107 | }
108 | {%-for reqProp in schema.requiredProperties |sort %}
109 | if (!json.HasMember("{{reqProp}}"))
110 | {
111 | throw {{exception}}("Missing '{{reqProp}}' property");
112 | }
113 | {%-endfor%}
114 | }
115 | {%-endif%}
116 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/array.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%import 'loader.jinja2' as loader with context-%}
2 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
3 | {%-set std = resolver.cpp_resolve_namespace(['std']) %}
4 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
5 | {%-if schema.GetItemSchema()['$ref'] %}
6 | // Array uses items reference
7 | {%-set itemtype = loader.Reference(resolver, schema.GetItemSchema()['$ref']) %}
8 | {%-else%}
9 | {%-set itemtype = className+'::Item'%}
10 |
11 | {{loader.Class('cpp',
12 | resolver,
13 | resolver.append_to_namespace(ns, Name),
14 | 'Item',
15 | schema.GetItemSchema()) }}
16 | {%endif%}
17 |
18 | {{className}}::{{Name}}({{resolver.cpp_resolve_namespace(['std'])}}vector<{{itemtype}}> arr)
19 | {
20 | SetArray(arr);
21 | }
22 |
23 | {%if schema.minItems is not defined or schema.minItems == 0 -%}
24 | {{className}}::{{Name}}()
25 | {
26 |
27 | }
28 | {%-endif%}
29 |
30 | void {{className}}::SetArray(const {{resolver.cpp_resolve_namespace(['std'])}}vector<{{itemtype}}>& arr)
31 | {
32 | {%-if schema.maxItems is defined %}
33 | if (arr.size() > {{schema.maxItems}})
34 | {
35 | throw {{exception}}("The array is shorter than {{className}}::MIN_ITEMS={{schema.minItems}}");
36 | }
37 | {%-endif%}
38 | {%-if schema.minItems is defined %}
39 | if (arr.size() < {{schema.minItems}})
40 | {
41 | throw {{exception}}("The array is longer than {{className}}::MAX_ITEMS={{schema.maxItems}}");
42 | }
43 | {%-endif%}
44 | _arr = arr;
45 | for ({{itemtype}}& el : _arr)
46 | {
47 | el.SetHandle(_handle);
48 | }
49 | }
50 |
51 | {{resolver.cpp_resolve_namespace(['std'])}}vector<{{itemtype}}> {{className}}::GetArray() const
52 | {
53 | return _arr;
54 | }
55 |
56 | void {{className}}::Append(const {{itemtype}}& item)
57 | {
58 | {%-if schema.maxItems is defined %}
59 | if (_arr.size() == {{schema.maxItems}})
60 | {
61 | throw {{std}}out_of_range("Adding to {{className}} would cause it to be longer than {{className}}::MAX_ITEMS={{schema.maxItems}}");
62 | }
63 | {%-endif%}
64 | _arr.push_back(item);
65 | _arr.back().SetHandle(_handle);
66 | }
67 |
68 | {{className}} {{className}}::FromJson(const {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& json)
69 | {
70 | if (!json.IsArray())
71 | {
72 | throw {{exception}}("The JSON wasn't an array");
73 | }
74 | {{resolver.cpp_resolve_namespace(['std'])}}vector<{{itemtype}}> arr;
75 | {{exception}}Collection exceptionCollection;
76 | unsigned i = 0;
77 | for (auto& v : json.GetArray())
78 | {
79 | try
80 | {
81 | arr.push_back({{itemtype}}::FromJson(v));
82 | }
83 | catch (const {{exception}}& e)
84 | {
85 | exceptionCollection.AddException(e, std::to_string(i));
86 | }
87 | i++;
88 | }
89 | if (exceptionCollection.IsExceptional())
90 | {
91 | throw exceptionCollection;
92 | }
93 | return {{className}}(arr);
94 | }
95 |
96 | void {{className}}::ToJson({{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
97 | {
98 | value.SetArray();
99 | for (const {{itemtype}}& el : _arr)
100 | {
101 | {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value elementValue;
102 | el.ToJson(elementValue, allocator);
103 | value.PushBack(elementValue, allocator);
104 | }
105 | }
106 |
107 | void {{className}}::SetHandle(const std::string& handle)
108 | {
109 | _handle = handle;
110 |
111 | for ({{itemtype}}& el : _arr)
112 | {
113 | el.SetHandle(handle);
114 | }
115 | }
116 |
117 | std::string {{className}}::GetHandle() const
118 | {
119 | return _handle;
120 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/python.py:
--------------------------------------------------------------------------------
1 | import json
2 | import abc
3 | import stringcase
4 | import os.path
5 |
6 | from jacobsjinjatoo import templator
7 | from . import schemawrappers
8 | from . import json_example
9 |
10 | class ResolverBaseClass(abc.ABC):
11 |
12 | @abc.abstractmethod
13 | def py_include_statement(self, reference):
14 | """Should return the include statement needed to acquire the object representing the
15 | schema pointed to at `reference`. Example, "from schema_foo import Foo"
16 | """
17 | pass
18 |
19 | @abc.abstractmethod
20 | def py_class_name(self, reference):
21 | """Should return the class name for the object representing the schema pointed to at `reference`.
22 | For example, "schema_foo.Foo"
23 | """
24 | pass
25 |
26 | @abc.abstractmethod
27 | def py_filename(self, reference):
28 | """Should return the name of the filename holding the python class representing the schema pointed to
29 | at `reference`. For example, "schema_foo.py"
30 | """
31 | pass
32 |
33 |
34 | class GeneratorFromSchema(object):
35 |
36 | def __init__(self, output_dir, resolver=None):
37 | self.output_dir = output_dir
38 | self.resolver = resolver
39 |
40 | def GetDeps(self, schema):
41 | return []
42 |
43 | def Generate(self, schema, root, class_name, filename_base):
44 | generator = templator.CodeTemplator(self.output_dir)
45 | generator.add_template_package('jsonschemacodegen.templates.python')
46 |
47 | args = {
48 | "Name": class_name,
49 | "schema": schemawrappers.SchemaFactory(schema, root),
50 | }
51 | return generator.render_template(template_name="file.py.jinja2", output_name="{}.py".format(filename_base), resolver=self.resolver, **args)
52 |
53 | def Examples(self, schema, root):
54 | wrapped_schema = schemawrappers.SchemaFactory(schema, root)
55 | number_of_examples = wrapped_schema.GetExampleCombos(self.resolver)
56 | examples = []
57 | examples.append(wrapped_schema.Example(self.resolver, schemawrappers.ExampleIndex(0)))
58 | examples.append(wrapped_schema.Example(self.resolver, schemawrappers.ExampleIndex(-1)))
59 | show_examples = min(number_of_examples, 20)
60 | example_step = int(number_of_examples/show_examples)
61 | index = example_step
62 | for _ in range(0, show_examples):
63 | examples.append(wrapped_schema.Example(self.resolver, schemawrappers.ExampleIndex(index)))
64 | index += example_step
65 | return sorted(list(set([json.dumps(x) for x in examples])))
66 |
67 | def GenerateTest(self, schema, root, class_name, filename_base, path):
68 | filename = self.resolver.py_test_filename(path)
69 | generator = templator.CodeTemplator(os.path.join(self.output_dir, os.path.dirname(filename)))
70 | generator.add_template_package('jsonschemacodegen.templates.python')
71 | wrapped_schema = schemawrappers.SchemaFactory(schema, root)
72 | args = {
73 | "Name": class_name.split('.')[-1],
74 | "schema": wrapped_schema,
75 | "examples": self.Examples(schema, root),
76 | "class": class_name,
77 | "path": path,
78 | "objType": path.split("/")[-2]
79 | }
80 | return generator.render_template(template_name="test.py.jinja2", output_name=os.path.basename(filename), resolver=self.resolver, **args)
81 |
82 | def GenerateFromPath(self, schema, path):
83 | assert(self.resolver)
84 | class_name = self.resolver.py_class_name(path).split('.')[-1]
85 | filename_base = self.resolver.py_filename(path)
86 | return self.Generate(schema, class_name, filename_base)
87 |
88 | def GenerateTestFromPath(self, schema, root, path):
89 | assert(self.resolver)
90 | class_name = self.resolver.py_class_name(path)
91 | filename_base = self.resolver.py_filename(path)
92 | return self.GenerateTest(schema, root, class_name, filename_base, path)
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/numeric.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'constraints.jinja2' as constraint-%}
2 | {%-if schema.type == 'integer'%}{%-set rjtype = 'int'%}{%-else%}{%-set rjtype = 'number'%}{%-endif-%}
3 | {%-if schema.type == 'integer'%}{%-set cpptype = 'int'%}{%-else%}{%-set cpptype = 'double'%}{%-endif%}
4 | {%-set std = resolver.cpp_resolve_namespace(['std']) %}
5 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
6 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
7 | {%if schema.const is not defined%}
8 | {{className}}::{{Name}}({{cpptype}} value)
9 | {
10 | Set(value);
11 | }
12 | {%endif%}
13 | {%-if schema.default is defined or schema.const is defined%}
14 | {%set emptyConstructor = true %}
15 | {{className}}::{{Name}}()
16 | {
17 | Set({%-if schema.const is defined-%}{{constraint.ExprName('const')}}{%-else-%}{{constraint.ExprName('default')}}{%-endif-%});
18 | }
19 | {%-endif%}
20 |
21 | {{className}}::operator {{cpptype}}() const
22 | {
23 | return Get();
24 | }
25 |
26 | {{className}}& {{className}}::operator=({{cpptype}} value)
27 | {
28 | Set(value);
29 | return *this;
30 | }
31 | {%for origNs in originalNamespace %}
32 | namespace {{origNs}} {
33 | {%-endfor%}
34 | {{std}}ostream& operator<<({{std}}ostream& os, const {{className}}& num)
35 | {
36 | os << num._value;
37 | return os;
38 | }
39 |
40 | bool operator< (const {{className}}& left, const {{className}}& right)
41 | {
42 | return left._value < right._value;
43 | }
44 |
45 | {{std}}size_t hash_value(const {{className}}& num)
46 | {
47 | return boost::hash_value(num._value);
48 | }
49 | {%for origNs in originalNamespace %}}{%-endfor%} // end namespaces
50 |
51 | {{className}}& {{className}}::Set({{cpptype}} value)
52 | {
53 | Validate(value);
54 | _value = value;
55 | return *this;
56 | }
57 |
58 | {{cpptype}} {{className}}::Get() const
59 | {
60 | return _value;
61 | }
62 |
63 | void {{className}}::Validate({{cpptype}} testValue)
64 | {
65 | {%-if schema.minimum is defined %}
66 | if (testValue < {{constraint.ExprName('minimum')}}) throw {{exception}}("Value was less than {{schema.minimum}}");
67 | {%-endif%}
68 | {%-if schema.exclusiveMinimum is defined %}
69 | if (testValue <= {{constraint.ExprName('exclusiveMinimum')}}) throw {{exception}}("Value was less than or equal to {{schema.exclusiveMinimum}}");
70 | {%-endif%}
71 | {%-if schema.maximum is defined %}
72 | if (testValue > {{constraint.ExprName('maximum')}}) throw {{exception}}("Value was more than {{schema.maximum}}");
73 | {%-endif%}
74 | {%-if schema.exclusiveMaximum is defined %}
75 | if (testValue >= {{constraint.ExprName('exclusiveMaximum')}}) throw {{exception}}("Value was less than or equal to {{schema.exclusiveMaximum}}");
76 | {%-endif%}
77 | {%-if schema.multipleOf is defined %}
78 | if ((testValue % {{constraint.ExprName('multipleOf')}}) != 0) throw {{exception}}("Value was less than or equal to {{schema.exclusiveMaximum}}");
79 | {%-endif%}
80 | {%-if schema.const is defined%}
81 | if (testValue != {{constraint.ExprName('const')}}) throw {{exception}}("Value was not {{schema.const}}");
82 | {%-endif%}
83 | }
84 |
85 | {{className}} {{className}}::FromJson(const {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& json)
86 | {
87 | if (!(json.Is{{rjtype | UpperCamelCase}}()))
88 | {
89 | throw {{exception}}("Wasn't a {{rjtype}}");
90 | }
91 |
92 | {{className}} newObject(json.Get{{cpptype | UpperCamelCase}}());
93 | return newObject;
94 | }
95 |
96 | {{className}} {{className}}::FromString(const {{std}}string& str)
97 | {
98 | return {{className}}({{resolver.cpp_resolve_namespace(['boost'])}}lexical_cast<{{cpptype}}>(str));
99 | }
100 |
101 | void {{className}}::ToJson({{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
102 | {
103 | value.Set{{cpptype | UpperCamelCase}}(_value);
104 | }
105 |
106 | void {{className}}::SetHandle(const std::string& handle)
107 | {
108 | _handle = handle;
109 | }
110 |
111 | std::string {{className}}::GetHandle() const
112 | {
113 | return _handle;
114 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/anyof.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%macro NestedObjectName(name) -%}
2 | {{-name | UpperCamelCase-}}
3 | {%-endmacro%}
4 | {%-macro ObjectType(propName, propSchema) -%}
5 | {%-if '$ref' in propSchema -%}
6 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
7 | {%-else-%}
8 | {{-NestedObjectName(propName)-}}
9 | {%-endif-%}
10 | {%-endmacro%}
11 | {%-macro ObjectName(propName, propSchema) -%}
12 | {%-if '$ref' in propSchema -%}
13 | {{-resolver.cpp_get_name(propSchema['$ref'])|UpperCamelCase-}}
14 | {%-else-%}
15 | {{-NestedObjectName(propName)-}}
16 | {%-endif-%}
17 | {%-endmacro%}
18 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
19 |
20 | /*! \class {{Name}}
21 | * \brief Optional union of: {%for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}{{ObjectName(componentName, s)}}{%if not loop.last%}, {%endif%}{%endfor %}
22 | {%-if schema.description %}
23 | * {{schema.description}}
24 | {%-endif%}
25 | */
26 | class {{Name}}
27 | {
28 | public:
29 | {%-import 'loader.jinja2' as loader%}
30 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
31 | {{loader.Class('hpp', resolver, [Name], componentName, s) | indent(4) }}
32 |
33 | {%endfor%}
34 |
35 | /*! Constructor for {{Name}}
36 | * Since all components are optional, this is an empty constructor.
37 | */
38 | {{Name}}();
39 | virtual ~{{Name}}() = default;
40 |
41 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
42 | /*! \fn boost::optional<{{ObjectType(componentName, s)}}> Get{{ObjectName(componentName, s)}}() const
43 | * \brief Gets the {{ObjectType(componentName, s)}} component of the {{Name}} object, if present.
44 | */
45 | boost::optional<{{ObjectType(componentName, s)}}> Get{{ObjectName(componentName, s)}}() const;
46 |
47 | /*! \fn void Set{{ObjectName(componentName, s)}}(const {{ObjectType(componentName, s)}}& component)
48 | * \brief Sets the {{ObjectType(componentName, s)}} component of {{Name}} object.
49 | * \param {{ObjectType(componentName, s)}} component is copied into the storage for this class.
50 | */
51 | void Set{{ObjectName(componentName, s)}}(const {{ObjectType(componentName, s)}}& component);
52 | {%endfor%}
53 |
54 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
55 | * \brief Deserializes JSON into a new instance of the {{Name}} object.
56 | * \param json is the RapidJSON value which must be of object type and conforming to the schema.
57 | * \returns {{Name}}
58 | */
59 | static {{Name}} FromJson(const rapidjson::Value& json);
60 |
61 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
62 | * \brief Serializes the combination of any set components to JSON
63 | * \param value is the RapidJSON value which will be modified to contain the serialization
64 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
65 | */
66 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
67 |
68 | /*! Sets a string handle associated with this {{Name}} instance.
69 | * This gets called by a parent object after creating an instance that is used for an object's property.
70 | * \param handle is the string name.
71 | */
72 | void SetHandle(const std::string& handle);
73 |
74 | /*! Gets the string handle associated with this {{Name}} instance.
75 | * This is often the property name used in a JSON-object parent.
76 | * It may be empty.
77 | * \returns the handle string
78 | */
79 | std::string GetHandle() const;
80 | private:
81 | {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
82 | boost::optional<{{ObjectType(componentName, s)}}> {{componentName|privatize}};
83 | {%-endfor%}
84 | std::string _handle;
85 |
86 | {%-if schema.requiredProperties | length > 0 %}
87 |
88 | /*! Checks to make sure any required properties defined in the 'anyOf' list are missing.
89 | * \param json Is completed JSON-object structure.
90 | * \throws {{exception}} If a property is missing
91 | */
92 | static void ThrowIfMissingRequiredProperties(const rapidjson::Value& json);
93 | {%-endif%}
94 | };
95 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/array.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader-%}
2 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
3 | {%-if schema.GetItemSchema()['$ref'] %}
4 | {%-set itemtype = loader.Reference(resolver, schema.GetItemSchema()['$ref']) %}
5 | {%-else-%}
6 | {%-set itemtype = 'Item'-%}
7 | {%-endif%}
8 | /*! \class {{Name}}
9 | * \brief Wrapper around an array containing {{itemtype}}
10 | {%-if schema.description %}
11 | * {{schema.description}}
12 | {%-endif%}
13 | */
14 | class {{Name}}
15 | {
16 | public:
17 | {%-if schema.GetItemSchema()['$ref'] is not defined %}
18 | {{loader.Class('hpp', resolver, [Name], 'Item', schema.GetItemSchema()) | indent(4) }}
19 | {%-endif%}
20 |
21 | {%-if schema.maxItems is defined %}
22 | static constexpr int MAX_ITEMS = {{schema.maxItems}};
23 | {%-endif%}
24 | {%-if schema.minItems is defined %}
25 | static constexpr int MIN_ITEMS = {{schema.minItems}};
26 | {%-endif%}
27 |
28 | /*! Constructor for {{Name}}
29 | * \param arr Vector of {{itemtype}} for initialization. {%if schema.minItems%}Must have at least {{schema.minItems}} elements.{%endif%}{%if schema.maxItems%}Must have at least {{schema.maxItems}} elements.{%endif%}
30 | * \throw {{exception}} if the length of the vector is incorrect
31 | */
32 | {{Name}}(std::vector<{{itemtype}}> arr);
33 |
34 | {%-if schema.minItems is not defined or schema.minItems == 0 %}
35 | /*! Constructor for {{Name}}
36 | * Constructs an empty array object
37 | */
38 | {{Name}}();
39 | {%-endif%}
40 |
41 | virtual ~{{Name}}() = default;
42 |
43 | /*! \fn SetArray(const std::vector<{{itemtype}}>& arr)
44 | * \param arr resets the array to the provided vector {%if schema.minItems%}Must have at least {{schema.minItems}} elements.{%endif%}{%if schema.maxItems%}Must have at least {{schema.maxItems}} elements.{%endif%}
45 | {%-if schema.minItems is defined or schema.maxItems is defined %}
46 | * \throw {{exception}} if the length of the vector is incorrect
47 | {%-endif%}
48 | */
49 | void SetArray(const std::vector<{{itemtype}}>& arr);
50 |
51 | /*! \fn std::vector<{{itemtype}}> GetArray() const
52 | * \brief Returns a vector of all the {{itemtype}} items
53 | * \return std::vector<{{itemtype}}>
54 | */
55 | std::vector<{{itemtype}}> GetArray() const;
56 |
57 | /*! \fn void Append(const {{itemtype}}& item)
58 | * \brief Adds an {{itemtype}} item to the array
59 | * \param item for appending
60 | {%-if schema.maxItems is defined %}
61 | * \throw std::out_of_range if adding the item would cause the length to be greater than {{Name}}::MAX_ITEMS={{schema.maxItems}}
62 | {%-endif%}
63 | */
64 | void Append(const {{itemtype}}& item);
65 |
66 | {# TODO: Allow this class to be iteratable #}
67 |
68 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
69 | * \brief Deserializes JSON into a new instance of the {{Name}} object.
70 | * \param json is the RapidJSON value which must be of array type and conforming to the schema.
71 | * \throw {{exception}} If the JSON value isnt an array.
72 | * \throw {{exception}}Collection If any of the components dont deserialize correctly according to their schemas.
73 | * \returns {{Name}}
74 | */
75 | static {{Name}} FromJson(const rapidjson::Value& json);
76 |
77 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
78 | * \brief Serializes the array and its elements to JSON
79 | * \param value is the RapidJSON value which will be modified to contain the serialization
80 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
81 | */
82 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
83 |
84 | /*! Sets a string handle associated with this {{Name}} instance.
85 | * This gets called by a parent object after creating an instance that is used for an object's property.
86 | * \param handle is the string name.
87 | */
88 | void SetHandle(const std::string& handle);
89 |
90 | /*! Gets the string handle associated with this {{Name}} instance.
91 | * This is often the property name used in a JSON-object parent.
92 | * It may be empty.
93 | * \returns the handle string
94 | */
95 | std::string GetHandle() const;
96 | private:
97 | std::vector<{{itemtype}}> _arr;
98 | std::string _handle;
99 | };
100 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JSON-Schema Codegen
2 |
3 | This python library consumes JSON-Schema and generates C++ or Python code. It generates structures to hold the values defined in the schema, restricting the values according to the schema.
4 |
5 | ## Python Requirements for Code Generation
6 |
7 | These requirements should be satisfied when `pip3` installing `json-schema-codegen`.
8 |
9 | * python 3.7
10 | * jinja2
11 | * stringcase
12 |
13 | ## Installation
14 |
15 | ```sh
16 | pip3 install json-schema-codegen
17 | ```
18 |
19 | ## C++ Generated Code
20 |
21 | ### Supported Schema Features in C++ code generation
22 |
23 | A C++ class is generated for each schema node according to the schema's `type` property. Schemas without a `type` property, with the exception of combining operators `*Of`, are not supported.
24 |
25 | * type: string
26 | * minLength
27 | * maxLength
28 | * pattern
29 | * format=date-time (enforces ISO8601 format)
30 | * format=uuid (enables string object to be populated with a uuid)
31 | * type: string with enum
32 | * type: integer
33 | * maximum
34 | * minimum
35 | * exclusiveMaximum
36 | * exclusiveMinimum
37 | * multipleOf
38 | * type: number
39 | * maximum
40 | * minimum
41 | * exclusiveMaximum
42 | * exclusiveMinimum
43 | * multipleOf
44 | * type: boolean
45 | * type: null
46 | * type: array
47 | * items
48 | * minItems
49 | * maxItems
50 | * type: object
51 | * properties
52 | * required
53 | * allOf
54 | * anyOf
55 | * oneOf
56 |
57 | ##### References
58 |
59 | `$ref` references are supported for array items, object properties, allOf, anyOf, and oneOf. However, the caller must provide a "resolver" class which translates the reference into a class name and namespace.
60 |
61 | ### Dependencies of the C++ generated code
62 |
63 | * boost (boost::optional and boost::variant among others)
64 | * rapidjson 1.1
65 | * C++11
66 |
67 | ### Usage
68 | See [example_usage.py](./examples/example_usage.py) for a more elaborate example on generating C++ code.
69 |
70 | ```py
71 | import jsonschemacodegen.cpp as cpp
72 |
73 | simpleResolver = cpp.SimpleResolver()
74 | output_dir = "/tmp"
75 |
76 | generator = cpp.GeneratorFromSchema(src_output_dir=output_dir,
77 | header_output_dir=output_dir,
78 | resolver=simpleResolver,
79 | namespace=[],
80 | src_usings=[])
81 |
82 | sampleSchema = {"type": "string"}
83 |
84 | generator.Generate(sampleSchema, 'Example', 'example')
85 | ```
86 |
87 | ## Python Generated Code
88 |
89 | A Python3 class is generated for each schema node; the class encapsulating the data described by the schema. The class accepts in its constructor python primative data types that match the format described the the schema. Each class has a `Serializable` method which returns data in a format that can be serialized.
90 |
91 | JSON (de-)serialization does not happen in the actual class. This allows for flexibility to use other line-formats, for example, YAML.
92 |
93 | ### Supported schema features for generating Python code
94 |
95 | * type: string
96 | * minLength
97 | * maxLength
98 | * pattern
99 | * enum
100 | * type: integer
101 | * maximum
102 | * minimum
103 | * exclusiveMaximum
104 | * exclusiveMinimum
105 | * multipleOf
106 | * enum
107 | * type: number
108 | * maximum
109 | * minimum
110 | * exclusiveMaximum
111 | * exclusiveMinimum
112 | * multipleOf
113 | * enum
114 | * type: boolean
115 | * type: null
116 | * type: array
117 | * items
118 | * minItems
119 | * maxItems
120 | * type: object
121 | * properties
122 | * required
123 | * allOf
124 | * anyOf
125 | * oneOf
126 | * Component schemas with the `title` property.
127 |
128 | ### Example usage for generating Python code
129 |
130 | For a more elaborate example, see [example_python.py](./examples/example_python.py)
131 |
132 | ```py
133 | from jsonschemacodegen import python as pygen
134 | import json
135 |
136 | with open('schema.json') as fp:
137 | generator = pygen.GeneratorFromSchema('output_dir')
138 | generator.Generate(json.load(fp), 'Example', 'example')
139 | ```
140 |
141 | This example will create the file `output_dir/example.py` containing the Python3 class `Example` and nested classes as required.
142 |
143 | Using the generated code looks like this:
144 | ```py
145 | import example
146 | import json
147 |
148 | jsonText = '["an example string in an array"]'
149 |
150 | obj = example.Example(json.loads(jsonText))
151 |
152 | print(json.dumps(obj, default=lambda x: x.Serializable()))
153 | ```
154 |
155 | ## License
156 |
157 | GPLv2
158 |
159 |
160 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/string.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
2 | {%-set std = resolver.cpp_resolve_namespace(['std']) %}
3 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
4 | {%-if schema.const is not defined%}
5 | {{className}}::{{Name}}(const {{std}}string& value)
6 | {
7 | Set(value);
8 | }
9 |
10 | {{className}}::{{Name}}(const char* value)
11 | {
12 | Set(value);
13 | }
14 | {%endif%}
15 | {%-if schema.default is defined or schema.const is defined%}
16 | {{className}}::{{Name}}()
17 | {
18 | Set({%if schema.const is defined%}CONST_VALUE{%else%}DEFAULT_VALUE{%endif%});
19 | }
20 | {%elif schema.format is defined and schema.format == 'uuid'%}
21 | {{className}}::{{Name}}()
22 | {
23 | SetUuid();
24 | }
25 | {%-endif%}
26 | {{className}}::operator {{std}}string() const
27 | {
28 | return Get();
29 | }
30 |
31 | {{className}}& {{className}}::operator=(const {{std}}string& value)
32 | {
33 | Set(value);
34 | return *this;
35 | }
36 |
37 | {{className}}& {{className}}::operator=(const char* value)
38 | {
39 | Set(value);
40 | return *this;
41 | }
42 | {%for origNs in originalNamespace %}
43 | namespace {{origNs}} {
44 | {%-endfor%}
45 | {{std}}ostream& operator<<({{std}}ostream& os, const {{className}}& str)
46 | {
47 | os << str._value;
48 | return os;
49 | }
50 |
51 | bool operator< (const {{className}}& left, const {{className}}& right)
52 | {
53 | return left._value < right._value;
54 | }
55 |
56 | {{std}}size_t hash_value(const {{className}}& str)
57 | {
58 | return boost::hash_value(str._value);
59 | }
60 | {%for origNs in originalNamespace %}}{%-endfor%} // end namespaces
61 |
62 | {{className}}& {{className}}::Set(const {{std}}string& value)
63 | {
64 | Validate(value);
65 | _value = value;
66 | return *this;
67 | }
68 |
69 | {{className}}& {{className}}::Set(const char* value)
70 | {
71 | Validate(value);
72 | _value = value;
73 | return *this;
74 | }
75 |
76 | {%-if schema.format is defined and schema.format == 'uuid'%}
77 | {{className}}& {{className}}::SetUuid()
78 | {
79 | boost::uuids::random_generator generator;
80 | boost::uuids::uuid uuid = generator();
81 | Set(boost::uuids::to_string(uuid));
82 | return *this;
83 | }
84 | {%-endif-%}{# #}
85 |
86 | {{std}}string {{className}}::Get() const
87 | {
88 | return _value;
89 | }
90 |
91 | void {{className}}::Validate(const {{std}}string& testValue)
92 | {
93 | {%-if schema.maxLength is defined %}
94 | if (testValue.size() > {{className}}::MAX_LENGTH)
95 | {
96 | throw {{exception}}("The string is longer than {{className}}::MAX_LENGTH={{schema.maxLength}}");
97 | }
98 | {%-endif%}
99 | {%-if schema.minLength is defined %}
100 | if (testValue.size() < {{className}}::MIN_LENGTH)
101 | {
102 | throw {{exception}}("The string is shorter than {{className}}::MIN_LENGTH={{schema.minLength}}");
103 | }
104 | {%-endif%}
105 | {%-if schema.pattern is defined %}
106 | {{std}}regex regexPattern("{{schema.pattern}}");
107 | {{std}}smatch regexMatch;
108 | {{std}}regex_match(testValue, regexMatch, regexPattern);
109 | if (regexMatch.empty()) throw {{exception}}("The string value did not match the required regular expression pattern '{{schema.pattern}}'");
110 | {%-endif%}
111 | {%-if schema.const is defined %}
112 | if (testValue != CONST_VALUE)
113 | {
114 | throw {{exception}}("The value is not '{{schema.const}}'");
115 | }
116 | {%-endif%}
117 | }
118 |
119 | {{className}} {{className}}::FromJson(const {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& json)
120 | {
121 | if (!(json.IsString()))
122 | {
123 | throw {{exception}}("JSON wasn't a string");
124 | }
125 | {%if schema.const is defined%}
126 | Validate(json.GetString());
127 | return {{className}}();
128 | {%-else%}
129 | return {{className}}(json.GetString());
130 | {%-endif%}
131 | }
132 |
133 | {{className}} {{className}}::FromString(const {{std}}string& str)
134 | {
135 | {%if schema.const is defined-%}
136 | Validate(str);
137 | return {{className}}();
138 | {%-else-%}
139 | return {{className}}(str);
140 | {%-endif%}
141 | }
142 |
143 | void {{className}}::ToJson({{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
144 | {
145 | value.SetString(_value.c_str(), _value.size(), allocator);
146 | }
147 |
148 | void {{className}}::SetHandle(const std::string& handle)
149 | {
150 | _handle = handle;
151 | }
152 |
153 | std::string {{className}}::GetHandle() const
154 | {
155 | return _handle;
156 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/allof.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
2 | {%macro NestedObjectName(name) -%}
3 | {{className}}::{{-name | UpperCamelCase-}}
4 | {%-endmacro%}
5 | {%-macro ObjectType(propName, propSchema) -%}
6 | {%-if '$ref' in propSchema -%}
7 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
8 | {%-else-%}
9 | {{-NestedObjectName(propName)-}}
10 | {%-endif-%}
11 | {%-endmacro%}
12 | {%-macro ObjectName(propName, propSchema) -%}
13 | {%-if '$ref' in propSchema -%}
14 | {{-resolver.cpp_get_name(propSchema['$ref'])|UpperCamelCase-}}
15 | {%-else-%}
16 | {{-propName | UpperCamelCase-}}
17 | {%-endif-%}
18 | {%-endmacro%}
19 | {%-macro ComponentName(parentName, schema, i)-%}
20 | {%-if 'title' in schema -%}
21 | {{-schema.title|UpperCamelCase-}}
22 | {%-if schema.title|UpperCamelCase == parentName|UpperCamelCase-%}Component{%-endif-%}
23 | {%-if 'properties' in schema and schema.title in schema.properties-%}Object{%-endif-%}
24 | {%-elif 'oneOf' in schema%}
25 | {{-''-}}OneOfComponent{{-i-}}
26 | {%-elif 'type' in schema and schema['type'] == 'object' and schema['properties'] | length == 1 -%}
27 | {{-schema.PropertyKeys()[0] | UpperCamelCase-}}Component
28 | {%-else-%}
29 | {{-''-}}Component{{-i-}}
30 | {%-endif-%}
31 | {%-endmacro%}
32 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
33 |
34 | {%import 'loader.jinja2' as loader with context%}
35 | {%-for s in schema.allOf %}{%set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset%}
36 | {{loader.Class('cpp', resolver, [className], componentName, s) }}
37 | {%-endfor%}
38 |
39 |
40 | {{className}}::{{Name}}({%-for s in schema.allOf -%}
41 | {%-set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset-%}
42 | const {{ObjectType(componentName, s)}}& {{componentName|camelCase}}{%if not loop.last%}, {%endif-%}
43 | {%-endfor%}) : {%for s in schema.allOf -%}
44 | {%-set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset-%}
45 | {{componentName|privatize}}({{componentName|camelCase}}){%if not loop.last%}, {%endif-%}
46 | {%-endfor%}
47 | {
48 |
49 | }
50 |
51 | {%-for s in schema.allOf %}{%set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset%}
52 | {{ObjectType(componentName, s)}} {{className}}::Get{{ObjectName(componentName, s)}}() const
53 | {
54 | return {{componentName|privatize}};
55 | }
56 |
57 | void {{className}}::Set{{ObjectName(componentName, s)}}(const {{ObjectType(componentName, s)}}& component)
58 | {
59 | {{componentName|privatize}} = component;
60 | {{componentName|privatize}}.SetHandle(_handle);
61 | }
62 | {%endfor%}
63 |
64 | {{className}} {{className}}::FromJson(const rapidjson::Value& json)
65 | {
66 | {%-if schema.requiredProperties | length > 0 %}
67 | ThrowIfMissingRequiredProperties(json);
68 | {%-endif%}
69 | {%-for s in schema.allOf %}{%set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset%}
70 | {{ObjectType(componentName, s)}} init{{componentName}} = {{ObjectType(componentName, s)}}::FromJson(json);
71 | {%-endfor%}
72 |
73 | return {{className}}({%-for s in schema.allOf %}{%set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset%}init{{componentName}}{%if not loop.last%}, {%endif%}{%endfor%});
74 | }
75 |
76 | void {{className}}::ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const
77 | {
78 | if (!value.IsObject())
79 | {
80 | value.SetObject();
81 | }
82 |
83 | {%-for s in schema.allOf %}{%set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset%}
84 | {{componentName|privatize}}.ToJson(value, allocator);
85 | {%-endfor %}
86 | {%-if schema.requiredProperties | length > 0 %}
87 | ThrowIfMissingRequiredProperties(value);
88 | {%-endif%}
89 | }
90 |
91 | void {{className}}::SetHandle(const std::string& handle)
92 | {
93 | _handle = handle;
94 |
95 | // This 'allOf' object passes the handle through to its children objects
96 | {%for s in schema.allOf -%}
97 | {%-set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset-%}
98 | {{componentName|privatize}}.SetHandle(handle);
99 | {%-endfor%}
100 | }
101 |
102 | std::string {{className}}::GetHandle() const
103 | {
104 | return _handle;
105 | }
106 |
107 | {%-if schema.requiredProperties | length > 0 %}
108 |
109 | void {{className}}::ThrowIfMissingRequiredProperties(const rapidjson::Value& json)
110 | {
111 | if (!json.IsObject())
112 | {
113 | throw {{exception}}("Not an object");
114 | }
115 | {%-for reqProp in schema.requiredProperties | sort%}
116 | if (!json.HasMember("{{reqProp}}"))
117 | {
118 | throw {{exception}}("Missing '{{reqProp}}' property");
119 | }
120 | {%-endfor%}
121 | }
122 | {%-endif%}
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/axxof.py.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader-%}
2 |
3 | {%-macro Combination(schema)-%}
4 | {%-if 'allOf' in schema-%}
5 | allOf
6 | {%-elif 'anyOf' in schema-%}
7 | anyOf
8 | {%-endif-%}
9 | {%-endmacro-%}
10 |
11 | {%-macro ComponentType(schema, index, fullClassPath=None)-%}
12 | {%-if '$ref' in schema-%}
13 | {{resolver.py_class_name(schema['$ref'])}}
14 | {%-else-%}
15 | self.Component{{index}}
16 | {%-endif-%}
17 | {%-endmacro-%}
18 |
19 | {%-set fullClassPath -%}
20 | {%-if parentClass is not defined or parentClass is none-%}
21 | {{Name}}
22 | {%-else-%}
23 | {{parentClass}}.{{Name}}
24 | {%-endif-%}
25 | {%-endset-%}
26 |
27 | class {{Name}}(object):
28 | """This combines {{Combination(schema)}} {{schema[Combination(schema)] |count}} components.
29 | """
30 |
31 | {%-for comp in schema[Combination(schema)]%}
32 | {%-if '$ref' not in comp%}
33 | {%-set compClassName%}Component{{loop.index}}{%endset%}
34 | {{loader.Class(resolver, compClassName, comp, fullClassPath) |indent(4)}}
35 | {%-endif%}
36 | {%-endfor%}
37 |
38 | def __init__(self, *args):
39 | """Initialization for {{Name}} which combines {{Combination(schema)}} {{schema[Combination(schema)] |count}} components.
40 | """
41 | {%-for compSchema in schema[Combination(schema)]%}
42 | self._component{{loop.index}} = None
43 | {%-endfor%}
44 | if len(args) == 1 and isinstance(args[0], type(self)):
45 | {%-for compSchema in schema[Combination(schema)]%}
46 | self._component{{loop.index}} = args[0]._component{{loop.index}}
47 | {%-endfor%}
48 | elif len(args) == 1:
49 | {%-for compSchema in schema[Combination(schema)]%}
50 | try:
51 | self._component{{loop.index}} = {{ComponentType(compSchema, loop.index)}}(args[0])
52 | except:
53 | {%-if Combination(schema) == 'allOf'%}
54 | raise ValueError("Could not find {{ComponentType(compSchema, loop.index)}} in data")
55 | {%-else%}
56 | pass
57 | {%-endif%}
58 | {%-endfor%}
59 | else:
60 | {%-for compSchema in schema[Combination(schema)]%}
61 | self._component{{loop.index}} = None
62 | {%-endfor%}
63 | for arg in args:
64 | {%-for compSchema in schema[Combination(schema)]%}
65 | if self._component{{loop.index}} is None:
66 | if isinstance(arg, {{ComponentType(compSchema, loop.index)}}):
67 | self._component{{loop.index}} = arg
68 | else:
69 | try:
70 | self._component{{loop.index}} = {{ComponentType(compSchema, loop.index)}}(arg)
71 | except:
72 | pass
73 | {%-endfor%}
74 | {%-if Combination(schema) == 'allOf'%}
75 | {%-for compSchema in schema['allOf']%}
76 | if self._component{{loop.index}} is None:
77 | raise ValueError("Did not provide data for {{ComponentType(compSchema, loop.index)}}")
78 | {%-endfor-%}
79 | {%-endif%}
80 | {%-if schema.requiredProperties | length > 0 %}
81 | # Test to make sure item can still serialize with the required properties
82 | self.Serializable()
83 | {%-endif%}
84 |
85 | {%for compSchema in schema[Combination(schema)]%}
86 | def GetComponent{{loop.index}}(self):
87 | return self._component{{loop.index}}
88 |
89 | def SetComponent{{loop.index}}(self, data) -> {{fullClassPath}}:
90 | if isinstance(data, {{ComponentType(compSchema, loop.index)}}):
91 | self._component{{loop.index}} = data
92 | elif isinstance(data, dict):
93 | self._component{{loop.index}} = {{ComponentType(compSchema, loop.index)}}(data)
94 | else:
95 | raise ValueError("Didn't pass an valid representation of {{ComponentType(compSchema, loop.index)}}")
96 | return self
97 | {%endfor%}
98 | def Serializable(self) -> dict:
99 | obj = {}
100 | {%-for compSchema in schema[Combination(schema)]%}
101 | if self._component{{loop.index}} is not None:
102 | obj.update(self._component{{loop.index}}.Serializable())
103 | {%-endfor%}
104 | {%-if schema.requiredProperties | length > 0 %}
105 | for req_prop in {{schema.requiredProperties |sort}}:
106 | if req_prop not in obj:
107 | raise KeyError("Missing required property '{}' in serialized object".format(req_prop))
108 | {%-endif%}
109 | return obj
110 |
111 |
--------------------------------------------------------------------------------
/examples/example_usage.py:
--------------------------------------------------------------------------------
1 | import jsonschemacodegen.cpp as cpp
2 | from jsonschemacodegen.resolver import SimpleResolver
3 |
4 | if __name__ == '__main__':
5 | simpleResolver = SimpleResolver("myproject")
6 | output_dir = "output"
7 |
8 | # The generated C++ class will be in the namespace foo::bar (ie foo::bar::ClassName)
9 | namespace = ["foo", "bar"]
10 |
11 | # The generated source will be prefixed with
12 | # "using namespace foo::bar" and "using namespace std"
13 | usings = [["foo", "bar"], ["std"]]
14 |
15 | generator = cpp.GeneratorFromSchema(src_output_dir=output_dir,
16 | header_output_dir=output_dir,
17 | resolver=simpleResolver)
18 |
19 | schema = {
20 | "type": "object",
21 | "properties": {
22 | "aString": {
23 | "type": "string",
24 | },
25 | "aStringEnum": {
26 | "type": "string",
27 | "enum": ["a", "b", "c"],
28 | },
29 | "aNumber": {
30 | "type": "number",
31 | "exclusiveMaximum": 1.0,
32 | "exclusiveMinimum": 0.0,
33 | },
34 | "anArray": {
35 | "type": "array",
36 | "items": {
37 | "type": "string",
38 | "maxLength": 10,
39 | },
40 | "maxItems": 5
41 | },
42 | "aNullValue": {
43 | "type": "null",
44 | },
45 | "anExampleOfOneOf": {
46 | "oneOf": [
47 | {"type": "integer"},
48 | {"type": "boolean"},
49 | ]
50 | },
51 | "anExampleOfAnyOf": {
52 | "anyOf": [
53 | {
54 | "type": "object",
55 | "properties": {
56 | "foo": {
57 | "type": "integer",
58 | }
59 | },
60 | "required": [
61 | "foo"
62 | ]
63 | },
64 | {
65 | "type": "object",
66 | "properties": {
67 | "bar": {
68 | "type": "string",
69 | }
70 | },
71 | },
72 | ]
73 | },
74 | "anExampleOfAllOf": {
75 | "allOf": [
76 | {
77 | "type": "object",
78 | "properties": {
79 | "bunny": {
80 | "type": "integer",
81 | }
82 | },
83 | "required": [
84 | "bunny"
85 | ]
86 | },
87 | {
88 | "type": "object",
89 | "properties": {
90 | "rabbit": {
91 | "type": "boolean",
92 | }
93 | },
94 | },
95 | ]
96 | },
97 | }
98 | }
99 |
100 | print("Generated {}".format(generator.Generate(schema, "myproject#/example/object")))
101 |
102 | schemaWithRefs = {
103 | "oneOf": [
104 | {
105 | "type": "object",
106 | "properties": {
107 | "localReference": {"$ref": "#/components/schemas/localReference"},
108 | "externalReference": {"$ref": "other.yaml#/components/schemas/externalReference"},
109 | "arrayWithLocalRef": {
110 | "type": "array",
111 | "items": {"$ref": "#/components/schemas/localItem"},
112 | },
113 | },
114 | },
115 | {
116 | "type": "object",
117 | "properties": {
118 | "arrayWithExtRef": {
119 | "type": "array",
120 | "items": {"$ref": "other.yaml#/components/schemas/extItem"},
121 | },
122 | },
123 | },
124 | {"$ref": "other.yaml#/components/schemas/externalObject"},
125 | {"$ref": "#/components/schemas/localObject"},
126 | ]
127 | }
128 | print("Generated {}".format(generator.Generate(schemaWithRefs, "myproject#/example/objectFoo")))
129 |
130 | dateTimeSchema = {
131 | "type": "string",
132 | "format": "date-time"
133 | }
134 |
135 | print("Generated {}".format(generator.Generate(dateTimeSchema, "myproject#/example/dateTimeObject")))
136 |
137 | uuidSchema = {
138 | "type": "string",
139 | "format": "uuid"
140 | }
141 |
142 | print("Generated {}".format(generator.Generate(uuidSchema, "myproject#/example/uuidObject")))
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/string_enum.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
2 | /*! {{Name}} is a wrapper around a {{schema.type}}.
3 | * The value is limited to a set of enum values.
4 | {%-if schema.description %}
5 | * {{schema.description | doxygenify}}
6 | {%-endif%}
7 | */
8 | class {{Name}}
9 | {
10 | public:
11 | /*! Enumeration of possible values
12 | */
13 | enum class Value
14 | {
15 | {%-for opt in schema.enum %}
16 | {{opt |enumify}}{%if not loop.last%},{%endif%}
17 | {%-endfor%}
18 | };
19 |
20 | /*! The number of different values.
21 | * If this list corresponds to a list of values, it may be good to static_assert on the length of both lists
22 | */
23 | static constexpr int VALUE_ENUM_OPTION_COUNT = {{schema.enum | length}};
24 | {%-if schema.default is defined %}
25 | /*! This string represents the default JSON value.
26 | */
27 | static constexpr char DEFAULT_STRING[] = "{{schema.default}}";
28 |
29 | /*! This is the default enum value.
30 | * When the class is called with an empty constructor, this is the value that is used.
31 | */
32 | static constexpr Value DEFAULT_VALUE = Value::{{schema.default |enumify}};
33 | {%-endif%}
34 |
35 | /*! Constructor for {{Name}}.
36 | * \param value initial value
37 | */
38 | {{Name}}(Value value);
39 | {# #}
40 | {%-if schema.default is defined %}
41 | /*! Constructor that initializes the object with the {{schema.default |enumify}} value.
42 | */
43 | {{Name}}();
44 | {%-endif%}
45 |
46 | /*! Default destructor.
47 | */
48 | virtual ~{{Name}}() = default;
49 |
50 | /*! Getter operator.
51 | */
52 | operator Value() const;
53 |
54 | /*! Assignment operator.
55 | * \param value new value
56 | */
57 | {{Name}}& operator=(Value value);
58 |
59 | /*! Stream operator
60 | * \param os the output stream
61 | * \param v instance of {{Name}}
62 | */
63 | friend std::ostream& operator<<(std::ostream& os, const {{Name}}& v);
64 |
65 | /*! The less than comparison operator.
66 | * \param left A {{Name}} instance on the left size of the less than expression.
67 | * \param right A {{Name}} instance on the right side of the less than expression.
68 | * \return true if the less than expression is true.
69 | */
70 | friend bool operator< (const {{Name}}& left, const {{Name}}& right);
71 |
72 | /*! Produce a hash value of the object.
73 | * \param e A {{Name}} instance from which to produce a hash.
74 | * \return a hash number using a boost hashing algorithm.
75 | */
76 | friend std::size_t hash_value(const {{Name}}& e);
77 |
78 | /*! Sets the value to the provided enumerated Value.
79 | * \param value new value.
80 | */
81 | void Set(Value value);
82 |
83 | /*! Get the enumerated Value.
84 | */
85 | Value Get() const;
86 |
87 | /*! Returns the json string value for the enumerated value.
88 | * \param value is the value for which a string should be returned.
89 | */
90 | static std::string EnumToString(Value value);
91 |
92 | /*! Returns an enumerated value matching the provided string.
93 | * \param str is the string to match.
94 | * \throws std::out_of_range If the provided string does not match an enumerated value.
95 | */
96 | static Value StringToEnum(const std::string& str);
97 |
98 | /*! Initializes a {{Name}} object from JSON.
99 | * \param json JSON string value that maps to an enumerated value.
100 | * \throws {{exception}} If the JSON value isn't one of the supported string values.
101 | */
102 | static {{Name}} FromJson(const rapidjson::Value& json);
103 |
104 | /*! Initializes a {{Name}} object from a string value.
105 | * \param str must match one of the supported string values.
106 | * \throws {{exception}} if the provided value isn't a supported value.
107 | */
108 | static {{Name}} FromString(const std::string& str);
109 |
110 | /*! Returns a JSON string representation of this object.
111 | * \param value is modified with the JSON string representation.
112 | * \param allocator is the rapidjson allocator object from the parent document.
113 | */
114 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
115 |
116 | /*! Returns a string representation of this object.
117 | * \return string representation without quotes.
118 | */
119 | std::string ToString() const;
120 |
121 | /*! Sets a string handle associated with this {{Name}} instance.
122 | * This gets called by a parent object after creating an instance that is used for an object's property.
123 | * \param handle is the string name.
124 | */
125 | void SetHandle(const std::string& handle);
126 |
127 | /*! Gets the string handle associated with this {{Name}} instance.
128 | * This is often the property name used in a JSON-object parent.
129 | * It may be empty.
130 | * \returns the handle string
131 | */
132 | std::string GetHandle() const;
133 | private:
134 | Value _value;
135 | std::string _handle;
136 | };
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/oneof.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader with context-%}
2 | {%-set std = resolver.cpp_resolve_namespace(['std']) %}
3 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
4 | {%macro NestedObjectName(name) -%}
5 | {{className}}::{{-name | UpperCamelCase-}}
6 | {%-endmacro%}
7 | {%-macro ObjectType(propName, propSchema) -%}
8 | {%-if '$ref' in propSchema -%}
9 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
10 | {%-else-%}
11 | {{-NestedObjectName(propName)-}}
12 | {%-endif-%}
13 | {%-endmacro%}
14 | {%-set optionList -%}
15 | {%-for s in schema.oneOf -%}{%set optionName%}Option{{loop.index}}{%endset-%}
16 | {{ObjectType(optionName, s)}}{%if not loop.last%}, {%endif-%}
17 | {%-endfor -%}
18 | {%-endset%}
19 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
20 |
21 | {%-for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset%}
22 | {{loader.Class('cpp', resolver, [className], optionName, s) }}
23 | {%-endfor%}
24 |
25 | {%-for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset%}
26 | {{className}}::{{Name}}(const {{ObjectType(optionName, s)}}& obj) : _value(obj)
27 | {
28 |
29 | }
30 | {%endfor%}
31 |
32 | {%set commonType = schema.GetCommonType(resolver)%}
33 | {%-if commonType in ['boolean', 'integer', 'number', string] %}
34 | {%for origNs in originalNamespace %}
35 | namespace {{origNs}} {
36 | {%-endfor%}
37 | {{std}}ostream& operator<<({{std}}ostream& os, const {{className}}& inst)
38 | {
39 | {%for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset-%}
40 | {%if not loop.first%}
41 | else {%endif%}if (inst._value.which() == {{loop.index - 1}})
42 | {
43 | os << boost::get<{{ObjectType(optionName, s)}}>(inst._value);
44 | }
45 | {%-endfor %}
46 | return os;
47 | }
48 |
49 | {{std}}size_t hash_value(const {{className}}& inst)
50 | {
51 | {{std}}size_t seed = 0;
52 | {%for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset-%}
53 | {%if not loop.first%}
54 | else {%endif%}if (inst._value.which() == {{loop.index - 1}})
55 | {
56 | boost::hash_combine(seed, boost::get<{{ObjectType(optionName, s)}}>(inst._value));
57 | }
58 | {%-endfor %}
59 | return seed;
60 | }
61 | {%for origNs in originalNamespace %}}{%-endfor%} // end namespaces
62 | {%-endif%}
63 |
64 | boost::variant<{{optionList}}> {{className}}::Get() const
65 | {
66 | return _value;
67 | }
68 |
69 | void {{className}}::Set(const boost::variant<{{optionList}}>& variant)
70 | {
71 | _value = variant;
72 | {%for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset-%}
73 | {%if not loop.first%}
74 | else {%endif%}if (_value.which() == {{loop.index - 1}})
75 | {
76 | boost::get<{{ObjectType(optionName, s)}}>(_value).SetHandle(_handle);
77 | }
78 | {%-endfor %}
79 | }
80 |
81 | {%-if commonType in ['boolean', 'integer', 'number', string] %}
82 | {{className}} {{className}}::FromString(const std::string& str)
83 | {
84 | {{exception}}Collection exceptionCollection;
85 | {%for s in schema.oneOf -%}{%set optionName%}Option{{loop.index}}{%endset-%}
86 | try
87 | {
88 | auto obj = {{ObjectType(optionName, s)}}::FromString(str);
89 | return {{className}}(obj);
90 | }
91 | catch (const std::exception& e)
92 | {
93 | // {{optionName}} wasnt able to deserialize{%if not loop.last%}
94 | // but there are other option to try{%endif%}
95 | exceptionCollection.AddException(e);
96 | }
97 | {%endfor %}
98 |
99 | // Didnt deserialize
100 | throw {{exception}}(exceptionCollection.what());
101 | }
102 | {%-endif%}
103 |
104 | {{className}} {{className}}::FromJson(const rapidjson::Value& json)
105 | {
106 | {{exception}}Collection exceptionCollection;
107 | {%for s in schema.oneOf -%}{%set optionName%}Option{{loop.index}}{%endset-%}
108 | try
109 | {
110 | auto obj = {{ObjectType(optionName, s)}}::FromJson(json);
111 | return {{className}}(obj);
112 | }
113 | catch (const std::exception& e)
114 | {
115 | // {{optionName}} wasnt able to deserialize{%if not loop.last%}
116 | // but there are other option to try{%endif%}
117 | exceptionCollection.AddException(e);
118 | }
119 | {%endfor %}
120 |
121 | // Didnt deserialize
122 | throw {{exception}}(exceptionCollection.what());
123 | }
124 |
125 | void {{className}}::ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const
126 | {
127 | {%for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset-%}
128 | {%if not loop.first%}
129 | else {%endif%}if (_value.which() == {{loop.index - 1}})
130 | {
131 | boost::get<{{ObjectType(optionName, s)}}>(_value).ToJson(value, allocator);
132 | }
133 | {%-endfor %}
134 | }
135 |
136 | void {{className}}::SetHandle(const std::string& handle)
137 | {
138 | _handle = handle;
139 |
140 | {%for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset-%}
141 | {%if not loop.first%}
142 | else {%endif%}if (_value.which() == {{loop.index - 1}})
143 | {
144 | boost::get<{{ObjectType(optionName, s)}}>(_value).SetHandle(handle);
145 | }
146 | {%-endfor %}
147 | }
148 |
149 | std::string {{className}}::GetHandle() const
150 | {
151 | return _handle;
152 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/oneof.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader-%}
2 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
3 | {%macro NestedObjectName(name) -%}
4 | {{-name | UpperCamelCase-}}
5 | {%-endmacro%}
6 | {%-macro ObjectType(propName, propSchema) -%}
7 | {%-if '$ref' in propSchema -%}
8 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
9 | {%-else-%}
10 | {{-NestedObjectName(propName)-}}
11 | {%-endif-%}
12 | {%-endmacro%}
13 | {%-set optionList -%}
14 | {%-for s in schema.oneOf -%}{%set optionName%}Option{{loop.index}}{%endset-%}
15 | {{ObjectType(optionName, s)}}{%if not loop.last%}, {%endif-%}
16 | {%-endfor -%}
17 | {%-endset%}
18 | /*! {{Name}} is a wrapper around one of {{schema.oneOf|length}} different types.
19 | {%-if schema.description %}
20 | *
21 | {{schema.description | indent(1) }}
22 | *
23 | {%-endif%}
24 | */
25 | class {{Name}}
26 | {
27 | public:
28 | {%-for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset%}
29 | {{loader.Class('hpp', resolver, [Name], optionName, s) | indent(4) }}
30 |
31 | {%endfor%}
32 |
33 | {%-for s in schema.oneOf %}{%set optionName%}Option{{loop.index}}{%endset%}
34 | /*! Constructor that initializes the {{Name}} object with a {{ObjectType(optionName, s)}} instance.]
35 | */
36 | {{Name}}(const {{ObjectType(optionName, s)}}& obj);
37 | {%endfor%}
38 | virtual ~{{Name}}() = default;
39 |
40 | {%-set commonType = schema.GetCommonType(resolver)%}
41 | {%-if commonType in ['boolean', 'integer', 'number', string] %}
42 | /*! Stream operator.
43 | */
44 | friend std::ostream& operator<<(std::ostream& os, const {{Name}}& inst);
45 | {%-endif%}
46 |
47 | /*! Produce a hash value of the object.
48 | * \param inst A {{Name}} instance from which to produce a hash.
49 | * \return a hash number using a boost hashing algorithm.
50 | */
51 | friend std::size_t hash_value(const {{Name}}& inst);
52 |
53 | /*! Gets a variant object containing whatever instance of whatever object has been set.
54 | */
55 | boost::variant<{{optionList}}> Get() const;
56 |
57 | {#{%-for s in schema.oneOf -%}{%set optionName%}Option{{loop.index}}{%endset-%}
58 | /*! Asserts that the type provided is a {{ObjectType(optionName, s)}}.
59 | * Asserts that when `object.Get().which() == {{loop.index - 1}}` the expression `boost::get<{{ObjectType(optionName, s)}}>(object.Get())` is valid.
60 | * This is static_assertion, so it will break at compile time, which is desireable.
61 | * \code
62 | boost::variant<{{optionList}}> v = instance.Get();
63 | if (v.which() == {{loop.index - 1}})
64 | {
65 | {{Name}}::StaticAssertWhich{{loop.index - 1}}Type<{{ObjectType(optionName, s)}}>();
66 | {{ObjectType(optionName, s)}} opt = boost::get<{{ObjectType(optionName, s)}}>(v);
67 | // do other stuff with opt
68 | }
69 | * \endcode
70 | */
71 | template
72 | static inline void StaticAssertWhich{{loop.index - 1}}Type()
73 | {
74 | static_assert(std::is_same::value, "Type {{loop.index - 1}} is not {{ObjectType(optionName, s)}}");
75 | }
76 | {%-endfor -%}#}
77 |
78 | /*! Sets the variant object.
79 | * \fn void Set(const boost::variant<{{optionList}}>& variant)
80 | * \param variant An instance of one of these types must be passed: {{optionList}}
81 | */
82 | void Set(const boost::variant<{{optionList}}>& variant);
83 |
84 | {%-if commonType in ['boolean', 'integer', 'number', string] %}
85 | /*! \fn {{Name}} FromString(const std::string& str)
86 | * \brief Performs a "lexical cast" on the string to create a new {{Name}} object
87 | * \param const std::string& str stringified representation of a {{schema.GetCommonType(resolver)}}
88 | * \throw {{exception}} If the string cannot be cast or does not JSON-schema validate
89 | * \return {{Name}}
90 | */
91 | static {{Name}} FromString(const std::string& str);
92 | {%-endif%}
93 |
94 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
95 | * \brief Deserializes a JSON value into a new instance of {{Name}}.
96 | * \param json is the RapidJSON value.
97 | * \throw {{exception}} If the JSON didn't validate according to the schema of one of the component objects.
98 | * \returns {{Name}}
99 | */
100 | static {{Name}} FromJson(const rapidjson::Value& json);
101 |
102 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
103 | * \brief Sets 'value' to whatever variant object is set
104 | * \param value is the RapidJSON value which will be modified to contain the serialization
105 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
106 | */
107 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
108 |
109 | /*! Sets a string handle associated with this {{Name}} instance.
110 | * This gets called by a parent object after creating an instance that is used for an object's property.
111 | * \param handle is the string name.
112 | */
113 | void SetHandle(const std::string& handle);
114 |
115 | /*! Gets the string handle associated with this {{Name}} instance.
116 | * This is often the property name used in a JSON-object parent.
117 | * It may be empty.
118 | * \returns the handle string
119 | */
120 | std::string GetHandle() const;
121 | private:
122 | boost::variant<{{optionList}}> _value;
123 | std::string _handle;
124 | };
125 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/object.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'loader.jinja2' as loader-%}
2 | {%-import 'propname.jinja2' as helper-%}
3 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset-%}
4 | {%macro NestedObjectName(name) -%}
5 | {%-if name|UpperCamelCase != Name|UpperCamelCase%}{{-name | UpperCamelCase-}}{%else%}{{name | UpperCamelCase}}Property{%endif-%}
6 | {%-endmacro%}
7 | {%-macro ObjectType(propName, propSchema) -%}
8 | {%-if '$ref' in propSchema -%}
9 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
10 | {%-else-%}
11 | {{-NestedObjectName(propName)-}}
12 | {%-endif-%}
13 | {%-endmacro%}
14 | /*! {{Name}} is a wrapper around an object.
15 | * \brief Wrapper around object with properties: {%for propName in schema.properties.keys() %}{{propName}}{%if not loop.last%}, {%endif%}{%endfor%}
16 | {%-if schema.description %}
17 | * {{schema.description}}
18 | {%-endif%}
19 | */
20 | class {{Name}}
21 | {
22 | public:
23 | {%-for propName, propSchema in schema.properties.items() %}
24 | /*! The key name used in the exported JSON object.
25 | */
26 | static constexpr auto {{helper.ConstPropertyName(propName)}} = "{{propName}}";
27 | {%endfor-%}
28 | {%-for propName, propSchema in schema.properties.items() %}
29 | {%-if '$ref' not in propSchema %}
30 | {{loader.Class('hpp', resolver, [Name], NestedObjectName(propName), propSchema) | indent(4) }}
31 | {%-endif%}
32 | {%endfor%}
33 |
34 | {%-if schema.RequiredList() | length > 0 %}
35 | /*! Constructor that sets required properties.
36 | */
37 | {{Name}}({%for propName, propSchema in schema.RequiredList()%}const {{ObjectType(propName, propSchema)}}& {{propName | camelCase}}{%if not loop.last%}, {%endif%}{%endfor%});
38 | {%-else%}
39 | /*! Constructor that initializes all properties to their defaults.
40 | */
41 | {{Name}}();
42 | {%if schema['properties'] | length == 1 %}
43 | {%-set propName = schema.PropertyKeys()[0] %}
44 | {%-set propSchema = schema.PropertyValues()[0] %}
45 | /*! Constructor for initializing object's single property.
46 | * \param {{propName | camelCase}} property to initialize.
47 | */
48 | {{Name}}(const {{ObjectType(propName, propSchema)}}& {{propName | camelCase}});
49 | {%if 'type' in propSchema and propSchema['type'] in ['boolean', 'integer', 'string', 'number'] %}
50 | /*! Constructor for initializing object's single {{propSchema.type}} property.
51 | * \param {{propName | camelCase}} value for initialization.
52 | */
53 | {{Name}}({{ {"boolean":"bool", "integer":"int", "string":"const std::string&", "number":"double"}[propSchema.type] }} {{propName | camelCase}});
54 | {%-endif%} {# propschema type #}
55 | {%-endif%} {# single property object #}
56 |
57 | {%-endif%}
58 |
59 | /*! Destructor.
60 | */
61 | virtual ~{{Name}}() = default;
62 | {%for propName, propSchema in schema.properties.items() %}
63 | {%-if propName in schema.required%}
64 | /*! Returns the required '{{propName}}' property of the object
65 | * \fn {{ObjectType(propName, propSchema)}} Get{{propName | UpperCamelCase}}() const
66 | * \returns {{ObjectType(propName, propSchema)}} Required property value
67 | */
68 | {{ObjectType(propName, propSchema)}} Get{{propName | UpperCamelCase}}() const;
69 | {%else%}
70 | /*! Returns the optional '{{propName}}' property of the object
71 | * \fn boost::optional<{{ObjectType(propName, propSchema)}}> Get{{propName | UpperCamelCase}}() const
72 | * \returns boost::optional<{{ObjectType(propName, propSchema)}}> Optional property value
73 | */
74 | boost::optional<{{ObjectType(propName, propSchema)}}> Get{{propName | UpperCamelCase}}() const;
75 | {%-endif%}
76 |
77 | /*! Sets the '{{propName}}' property of the object.
78 | * \fn void Set{{propName | UpperCamelCase}}(const {{ObjectType(propName, propSchema)}}& value)
79 | * \param value the value which is set as the {{propName}} property.
80 | */
81 | {{Name}}& Set{{propName | UpperCamelCase}}(const {{ObjectType(propName, propSchema)}}& value);
82 | {%-endfor%}
83 |
84 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
85 | * \brief Deserializes a JSON "object" value into a new instance of {{Name}}.
86 | * \param json is the RapidJSON value which must be of "object" type.
87 | * \throw {{exception}} If the JSON value wasn't an object.
88 | * \throw {{exception}}Collection if any property was missing or didn't validate.
89 | * \returns {{Name}}
90 | */
91 | static {{Name}} FromJson(const rapidjson::Value& json);
92 |
93 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
94 | * \brief Sets 'value' to a JSON object
95 | * \param value is the RapidJSON value which will be modified to contain the serialization
96 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
97 | */
98 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
99 |
100 | /*! Sets a string handle associated with this {{Name}} instance.
101 | * This gets called by a parent object after creating an instance that is used for an object's property.
102 | * \param handle is the string name.
103 | */
104 | void SetHandle(const std::string& handle);
105 |
106 | /*! Gets the string handle associated with this {{Name}} instance.
107 | * This is often the property name used in a JSON-object parent.
108 | * It may be empty.
109 | * \returns the handle string
110 | */
111 | std::string GetHandle() const;
112 | private:
113 | {%-for propName, propSchema in schema.properties.items() %}
114 | {%-if propName in schema.required %}
115 | {{ObjectType(propName, propSchema)}} {{propName | privatize}};
116 | {%-else%}
117 | boost::optional<{{ObjectType(propName, propSchema)}}> {{propName | privatize}};
118 | {%-endif%}
119 | {%-endfor%}
120 | std::string _handle;
121 | };
122 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/allof.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%macro NestedObjectName(name) -%}
2 | {{-name | UpperCamelCase-}}
3 | {%-endmacro%}
4 | {%-macro ObjectType(propName, propSchema) -%}
5 | {%-if '$ref' in propSchema -%}
6 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
7 | {%-else-%}
8 | {{-NestedObjectName(propName)-}}
9 | {%-endif-%}
10 | {%-endmacro%}
11 | {%-macro ObjectName(propName, propSchema) -%}
12 | {%-if '$ref' in propSchema -%}
13 | {{-resolver.cpp_get_name(propSchema['$ref'])|UpperCamelCase-}}
14 | {%-else-%}
15 | {{-NestedObjectName(propName)-}}
16 | {%-endif-%}
17 | {%-endmacro%}
18 | {%-macro ComponentName(parentName, schema, i)-%}
19 | {%-if 'title' in schema -%}
20 | {{-schema.title|UpperCamelCase-}}
21 | {%-if schema.title|UpperCamelCase == parentName|UpperCamelCase-%}Component{%-endif-%}
22 | {%-if 'properties' in schema and schema.title in schema.properties-%}Object{%-endif-%}
23 | {%-elif 'oneOf' in schema%}
24 | {{-''-}}OneOfComponent{{-i-}}
25 | {%-elif 'type' in schema and schema['type'] == 'object' and schema['properties'] | length == 1 -%}
26 | {{-schema.PropertyKeys()[0] | UpperCamelCase-}}Component
27 | {%-else-%}
28 | {{-''-}}Component{{-i-}}
29 | {%-endif-%}
30 | {%-endmacro%}
31 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
32 |
33 | /*! \class {{Name}}
34 | * \brief Mandatory union of: {%for s in schema.allOf %}{{ObjectName(ComponentName(Name, s, loop.index), s)}}{%if not loop.last%}, {%endif%}{%endfor %}.
35 | {%-if schema.description -%}
36 | * {{schema.description}}
37 | {%-endif%}
38 | {%-if schema.requiredProperties is defined and schema.requiredProperties | length > 0%}
39 | * The contained object must provide the following properties: {%for rp in schema.requiredProperties |sort%}`{{rp}}` {%endfor%}
40 | {%-endif%}
41 | */
42 | class {{Name}}
43 | {
44 | public:
45 | {%-import 'loader.jinja2' as loader%}
46 | {%-for s in schema.allOf %}
47 | {%-if '$ref' not in s %}
48 | {{loader.Class('hpp', resolver, [Name], ComponentName(Name, s, loop.index), s) | indent(4) }}
49 | {%-endif%}
50 | {%endfor%}
51 |
52 | /*! Constructor for {{Name}}
53 | * Requires instances of all components. The data from each component is copied into storage for this class.
54 | {%-for s in schema.allOf -%}
55 | {%-set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset-%}
56 | * \param {{componentName|camelCase}} Instance of {{ObjectType(componentName, s)}}
57 | {%-endfor%}
58 | */
59 | {{Name}}({%-for s in schema.allOf -%}
60 | {%-set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset-%}
61 | const {{ObjectType(componentName, s)}}& {{componentName|camelCase}}{%if not loop.last%}, {%endif-%}
62 | {%-endfor%});
63 | virtual ~{{Name}}() = default;
64 |
65 | {%-for s in schema.allOf %}{%set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset%}
66 | /*! \fn {{ObjectType(componentName, s)}} Get{{ObjectName(componentName, s)}}() const
67 | * \brief returns the {{ObjectType(componentName, s)}} component of the {{Name}} object
68 | */
69 | {{ObjectType(componentName, s)}} Get{{ObjectName(componentName, s)}}() const;
70 |
71 | /*! \fn void Set{{ObjectName(componentName, s)}}(const {{ObjectType(componentName, s)}}& component)
72 | * \brief Sets the {{ObjectType(componentName, s)}} component of {{Name}} object.
73 | * \param component is copied into the storage for this class.
74 | */
75 | void Set{{ObjectName(componentName, s)}}(const {{ObjectType(componentName, s)}}& component);
76 | {%endfor%}
77 |
78 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
79 | * \brief Deserializes JSON into a new instance of the {{Name}} object.
80 | * \param json is the RapidJSON value which must be of object type and conforming to the schema.
81 | * \throw {{exception}} If any of the components don't deserialize correctly according to their schemas.
82 | * \returns {{Name}}
83 | */
84 | static {{Name}} FromJson(const rapidjson::Value& json);
85 |
86 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
87 | * \brief Serializes the combined (allof) object to JSON
88 | * \param value is the RapidJSON value which will be modified to contain the serialization
89 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
90 | {%-if schema.requiredProperties is defined and schema.requiredProperties | length > 0%}
91 | * \throw {{exception}} When the resulting JSON object is missing one or more of the following properties: {%for rp in schema.requiredProperties |sort%}`{{rp}}` {%endfor%}
92 | {%-endif%}
93 | */
94 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
95 |
96 | /*! Sets a string handle associated with this {{Name}} instance.
97 | * This gets called by a parent object after creating an instance that is used for an object's property.
98 | * \param handle is the string name.
99 | */
100 | void SetHandle(const std::string& handle);
101 |
102 | /*! Gets the string handle associated with this {{Name}} instance.
103 | * This is often the property name used in a JSON-object parent.
104 | * It may be empty.
105 | * \returns the handle string
106 | */
107 | std::string GetHandle() const;
108 | private:
109 | {%-for s in schema.allOf %}{%set componentName%}{{ComponentName(Name, s, loop.index)}}{%endset%}
110 | {{ObjectType(componentName, s)}} {{componentName|privatize}};
111 | {%-endfor%}
112 | std::string _handle;
113 |
114 | {%-if schema.requiredProperties | length > 0 %}
115 |
116 | /*! Checks to make sure any required properties defined in the 'allOf' list are missing.
117 | * \param json Is completed JSON-object structure.
118 | * \throws {{exception}} If a property is missing
119 | */
120 | static void ThrowIfMissingRequiredProperties(const rapidjson::Value& json);
121 | {%-endif%}
122 | };
123 |
--------------------------------------------------------------------------------
/jsonschemacodegen/resolver.py:
--------------------------------------------------------------------------------
1 | from . import cpp
2 | from . import python as pyschema
3 | from . import json_example as jsex
4 | import stringcase
5 | from . import schemawrappers
6 | import yaml
7 | from copy import copy
8 |
9 | class TreeWalkerException(Exception):
10 | pass
11 |
12 | class SimpleResolver(cpp.ResolverBaseClass, pyschema.ResolverBaseClass, jsex.SchemaResolverBaseClass):
13 |
14 | def __init__(self, uri, root=None):
15 | self.uri = uri
16 | self.root = root
17 | self.usings = []
18 | self.rootNs = []
19 |
20 | def cpp_set_root_namespace(self, ns: list):
21 | self.rootNs = ns
22 |
23 | def cpp_add_using(self, using: list):
24 | self.usings.append(using)
25 |
26 | def cpp_get_root_namespace(self) -> list:
27 | return self.rootNs
28 |
29 | def cpp_get_usings(self) -> list:
30 | return self.usings
31 |
32 | def _get_reference_parts(self, reference) -> tuple:
33 | pkg = None
34 | fn = reference.split('#')[0] or None
35 | path = reference.split('#')[1]
36 | parts = path.split('/')
37 | if fn:
38 | pkg = fn.split('.')[0]
39 | return (pkg, parts[-2], stringcase.pascalcase(parts[-1]))
40 |
41 | def _get_reference_parts(self, reference) -> dict:
42 | assert('#' in reference), "Reference '{}' seemed malformed".format(reference)
43 | url, path = reference.split('#')
44 | theType, name = path.split('/')[-2:]
45 | return {
46 | "url": url,
47 | "uri": url,
48 | "path": path,
49 | "type": theType,
50 | "PascalType": stringcase.pascalcase(theType),
51 | "name": name,
52 | "PascalName": stringcase.pascalcase(name),
53 | "snake_name": stringcase.snakecase(name),
54 | "pkg": url is not None and url.split(".")[0] or None,
55 | }
56 |
57 | def cpp_get_header_dir(self, uri=None) -> str:
58 | uri = uri or self.uri
59 | pkg = uri.split(".")[0]
60 | return pkg
61 |
62 | def cpp_get_filename_base(self, reference) -> str:
63 | parts = self._get_reference_parts(reference)
64 | if parts['pkg'] not in [None, self.uri]:
65 | header_dir = self.cpp_get_header_dir(parts['uri'])
66 | fn_base = "{0}/{1}_{2}".format(header_dir, parts['type'].rstrip('s'), parts['snake_name'])
67 | else:
68 | fn_base = "{0}_{1}".format(parts['type'].rstrip('s'), parts['snake_name'])
69 | return fn_base
70 |
71 | def cpp_get_header(self, reference) -> str:
72 | header = "{}.hpp".format(self.cpp_get_filename_base(reference))
73 | return header
74 |
75 | def cpp_get_namespace(self, reference) -> list:
76 | parts = self._get_reference_parts(reference)
77 | ns = copy(self.rootNs)
78 | if parts['pkg'] is not None:
79 | ns.append(stringcase.lowercase(parts['pkg']))
80 | else:
81 | ns.append(self.uri)
82 | ns.append(stringcase.lowercase(parts['type'].rstrip('s')))
83 | return ns
84 |
85 | def cpp_get_name(self, reference) -> str:
86 | parts = self._get_reference_parts(reference)
87 | return parts['PascalName']
88 |
89 | def py_include_statement(self, reference) -> str:
90 | ref = self._get_reference_parts(reference)
91 | if ref['pkg'] is not None:
92 | return "import {pkg}.{type}_{name}".format(**ref)
93 | else:
94 | return "import {type}_{name}".format(**ref)
95 |
96 | def py_class_name(self, reference) -> str:
97 | ref = self._get_reference_parts(reference)
98 | if ref['pkg'] is not None:
99 | return "{pkg}.{type}_{name}.{PascalName}".format(**ref)
100 | else:
101 | return "{type}_{name}.{PascalName}".format(**ref)
102 |
103 | def py_filename(self, reference) -> str:
104 | ref = self._get_reference_parts(reference)
105 | return "{type}_{name}.py".format(**ref)
106 |
107 | def _walk_through_tree(self, tree, path) -> dict:
108 | assert(tree is not None), "No tree to walk through"
109 | walker = tree
110 | for p in [p for p in path.split('/') if len(p) > 0]:
111 | if p not in walker:
112 | treeTitle = 'info' in tree and 'title' in tree['info'] and tree['info']['title'] or 'UNKNOWN'
113 | treeId = 'id' in tree and tree['id'] or treeTitle
114 | raise TreeWalkerException("Could not resolve {} from '{}'".format(path, treeId))
115 | walker = walker[p]
116 | return walker
117 |
118 | def get_document(self, uri, encoding=None) -> dict:
119 | if '#' in uri:
120 | uri = uri.split('#')[0]
121 | with open(uri, 'r') as fp:
122 | if encoding == 'json' or (encoding is None and 'json' in uri):
123 | import json
124 | return json.load(fp)
125 | else:
126 | return yaml.load(fp, Loader=yaml.FullLoader)
127 |
128 | def _get_root(self, reference, root):
129 | parts = self._get_reference_parts(reference)
130 | if parts['uri']:
131 | root_doc = self.get_document(parts['uri'])
132 | elif root is not None:
133 | root_doc = root
134 | else:
135 | root_doc = self.root
136 | return root_doc
137 |
138 | def get_json(self, reference, root=None) -> dict:
139 | parts = self._get_reference_parts(reference)
140 | root_doc = self._get_root(reference, root)
141 | try:
142 | json_doc = self._walk_through_tree(root_doc, parts['path'])
143 | except TreeWalkerException as e:
144 | print(f"{self} Error in resolving {reference}: {e}")
145 | raise
146 | else:
147 | return json_doc
148 |
149 | def get_schema(self, reference, root=None) -> schemawrappers.SchemaBase:
150 | schemasRoot = self._get_root(reference, root)
151 | schema = schemawrappers.SchemaFactory(self.get_json(reference, root=root), schemasRoot)
152 | return schema
153 |
154 | def cpp_get_lib_ns(self):
155 | return []
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/datetime.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
2 | /*! {{Name}} is a wrapper around a {{schema.format}} implemented as a {{schema.type}}.
3 | {%-if schema.description %}
4 | * {{schema.description}}
5 | {%-endif%}
6 | */
7 | class {{Name}}
8 | {
9 | public:
10 | /*! Constructor.
11 | * \param value ISO8601 string representation of {{schema.format}} to be used for the initial value of {{Name}} instance.
12 | */
13 | {{Name}}(const std::string& value);
14 |
15 | /*! Constructor.
16 | * \param value ISO8601 string representation of {{schema.format}} to be used for the initial value of {{Name}} instance.
17 | */
18 | {{Name}}(const char* value);
19 |
20 | {%-if schema.default is defined %}
21 | /*! Constructor that uses the default value of "{{schema.default}}".
22 | */
23 | {%-else%}
24 | /*! Constructor that sets the objects value to the current date and time.
25 | */
26 | {%-endif%}
27 | {{Name}}();
28 |
29 | /*! Copy Constructor.
30 | * \param other is the source of the time for the new object.
31 | */
32 | {{Name}}(const {{Name}}& other);
33 |
34 | /*! Default destructor.
35 | */
36 | virtual ~{{Name}}() = default;
37 |
38 | /*! Operator to cast a {{Name}} instance to a string formatted as a ISO8601.
39 | */
40 | operator std::string() const;
41 |
42 | /*! Assignment operator to set the value of the {{Name}} instance from a ISO8601 string.
43 | * \param value the new ISO8601 string value.
44 | * \throw {{exception}} if the passed string value is not ISO8601 formatted.
45 | * \return reference to the {{Name}} instance
46 | */
47 | {{Name}}& operator=(const std::string& value);
48 |
49 | /*! Assignment operator to set the value of the {{Name}} instance from a ISO8601 string.
50 | * \param value the new ISO8601 string value.
51 | * \throw {{exception}} the passed string value is not ISO8601 formatted.
52 | * \return reference to the {{Name}} instance
53 | */
54 | {{Name}}& operator=(const char* value);
55 |
56 | /*! Assignment operator to set the value of the {{Name}} instance from a boost ptime.
57 | * \param value boost ptime object.
58 | * \return reference to the {{Name}} instance
59 | */
60 | {{Name}}& operator=(const boost::posix_time::ptime& value);
61 |
62 | /*! Provide the string value of the {{Name}} instance to a stream.
63 | * \param os the stream object
64 | * \param str an instance of {{Name}}
65 | * \return the same ostream input object
66 | */
67 | friend std::ostream& operator<<(std::ostream& os, const {{Name}}& str);
68 |
69 | /*! Set the value of the {{schema.format}}.
70 | * \param value ISO8601 {{schema.format}} representation.
71 | * \throw if the passed string isn't ISO8601 formatted.
72 | */
73 | void Set(const std::string& value);
74 |
75 | /*! Set the value of the {{schema.format}}.
76 | * \param value ISO8601 {{schema.format}} representation.
77 | * \throw if the passed string isn't ISO8601 formatted.
78 | */
79 | void Set(const char* value);
80 |
81 | /*! Sets the encapsulated time object.
82 | * \param datetime new time value
83 | */
84 | void Set(const boost::posix_time::ptime& datetime);
85 |
86 | /*! Sets the encapsulated time object to represent the time this is called.
87 | */
88 | void SetNow();
89 |
90 | /*! Sets the encapsulated time object to always represent the current time.
91 | * If this is called, repeated calles to Get() will always return time at the moment that Get() is called.
92 | */
93 | void SetCurrent{{schema.format | PascalCase}}();
94 |
95 | /*! Get the ISO8601 representation of the {{schema.format}}.
96 | * \return ISO8601 representation of the {{schema.format}}.
97 | */
98 | std::string GetString() const;
99 |
100 | /*!
101 | * Determines if calling Get() will return a fixed time or always the current time.
102 | * \return true if calling Get() will always return the current time. False indicates that calling Get() will return a static time.
103 | */
104 | bool IsCurrentTime() const;
105 |
106 | /*! Returns boost time object.
107 | * \return The contained time, either static or dynamic current time.
108 | */
109 | boost::posix_time::ptime Get() const;
110 |
111 | /*! Create a new {{Name}} object from a JSON string.
112 | * \throw if the JSON string wasn't ISO8601 {{schema.format}} formatted.
113 | * \return new {{Name}} object containing values from the JSON.
114 | * \param json JSON structure to use for creating new {{Name}} object.
115 | */
116 | static {{Name}} FromJson(const rapidjson::Value& json);
117 |
118 | /*! Create a new {{Name}} object from the string.
119 | * The string must be ISO8601 {{schema.format}} formatted.
120 | * \return new {{Name}} object containing the string.
121 | * \param str the string that will be contained in the new {{Name}} object.
122 | * \throw if the string didn't meet the schema's constraints.
123 | */
124 | static {{Name}} FromString(const std::string& str);
125 |
126 | /*! Serialize the {{Name}} instance into a JSON string.
127 | * \param value is modified to be a string type containing an ISO8601 {{schema.format}}.
128 | * \param allocator is used for rapidjson allocation.
129 | */
130 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
131 |
132 | /*! Sets a string handle associated with this {{Name}} instance.
133 | * This gets called by a parent object after creating an instance that is used for an object's property.
134 | * \param handle is the string name.
135 | */
136 | void SetHandle(const std::string& handle);
137 |
138 | /*! Gets the string handle associated with this {{Name}} instance.
139 | * This is often the property name used in a JSON-object parent.
140 | * It may be empty.
141 | * \returns the handle string
142 | */
143 | std::string GetHandle() const;
144 | private:
145 | boost::optional _value;
146 | std::string _handle;
147 | };
--------------------------------------------------------------------------------
/jsonschemacodegen/cpp.py:
--------------------------------------------------------------------------------
1 | import json
2 | import abc
3 | import stringcase
4 | import os
5 |
6 | from jacobsjinjatoo import templator
7 | from . import schemawrappers
8 |
9 | class ResolverBaseClass(abc.ABC):
10 |
11 | def cpp_resolve_namespace(self, ns: list, append='::') -> str:
12 | """ Figures out what a namespace should be for C++.
13 | @param ns is the full namespace of the object that should be resolved. For example, if the object is:
14 | d::e::f::Goo, then the ns param should be ['d', 'e', 'f'] with 'Goo' being the object name.
15 | @param append is a string that will be appended to the result only if there is some part of a namespace left.
16 | @returns a string that should go before the object. Using the described parameters, the result
17 | would be "f::"
18 | """
19 | assert(isinstance(ns, list))
20 | usings = self.cpp_get_usings()
21 | for n in ns:
22 | assert(isinstance(n, str)), "namespace is %s" % (ns)
23 | ret = ns
24 | nsString = "||".join(ns)
25 | for using in usings:
26 | if nsString.startswith("||".join(using)):
27 | abbr = ns[len(using):]
28 | if len(abbr) < len(ret):
29 | ret = abbr
30 | if len(ret) > 0:
31 | return "::".join(ret)+append
32 | else:
33 | return ""
34 |
35 | def append_to_namespace(self, ns: list, appendage) -> list:
36 | assert(isinstance(ns, list)), "Namespace isn't list it is %s" % (ns)
37 | newNs = ns.copy()
38 | newNs.append(appendage)
39 | return newNs
40 |
41 | @abc.abstractmethod
42 | def cpp_get_filename_base(self, reference):
43 | pass
44 |
45 | @abc.abstractmethod
46 | def cpp_get_header(self, reference: str) -> str:
47 | """ Given a $ref reference from a schema, return the name/path of the header file that should be included for the declaration
48 | of the represented object.
49 | """
50 | pass
51 |
52 | @abc.abstractmethod
53 | def cpp_get_namespace(self, reference: str) -> list:
54 | """Given a reference and the current usings statements, return the namespace of the object pointed to by the reference.
55 | If the namespace is not empty, also append the append string
56 | """
57 | pass
58 |
59 | @abc.abstractmethod
60 | def cpp_get_name(self, reference: str) -> str:
61 | """Given a reference, return the C++ object name pointed to by the reference.
62 | """
63 | pass
64 |
65 | def cpp_get_ns_name(self, reference: str) -> str:
66 | ns = self.cpp_get_namespace(reference)
67 | name = self.cpp_get_name(reference)
68 | if ns is None:
69 | return
70 | return "{}::{}".format("::".join(ns), name)
71 |
72 | @abc.abstractmethod
73 | def cpp_get_root_namespace(self) -> list:
74 | pass
75 |
76 | @abc.abstractmethod
77 | def cpp_get_usings(self) -> list:
78 | pass
79 |
80 | @abc.abstractmethod
81 | def cpp_get_lib_ns(self) -> list:
82 | pass
83 |
84 |
85 | class GeneratorFromSchema(object):
86 |
87 | def __init__(self, src_output_dir, header_output_dir, resolver):
88 | self.output_dir = {
89 | "src": src_output_dir,
90 | "header": header_output_dir,
91 | }
92 | assert(isinstance(resolver, ResolverBaseClass)), "Resolver is %s" % (resolver)
93 | self.resolver = resolver
94 |
95 | def _make_sure_directory_exists(self, output_key, dir_path):
96 | d = os.path.join(self.output_dir[output_key], dir_path)
97 | if not os.path.exists(d):
98 | os.makedirs(d)
99 |
100 | def GetDeps(self, schema):
101 | return schema.CppIncludes(self.resolver)
102 |
103 | def Generate(self, schema, path):
104 | retval = [None, None]
105 | srcGenerator = templator.CodeTemplator(self.output_dir['src']).add_template_package('jsonschemacodegen.templates.cpp')
106 | headerGenerator = templator.CodeTemplator(self.output_dir['header']).add_template_package('jsonschemacodegen.templates.cpp')
107 | args = {
108 | "Name": self.resolver.cpp_get_name(path),
109 | "schema": schemawrappers.SchemaFactory(schema),
110 | }
111 | headerFilename = self.resolver.cpp_get_header(path)
112 | self._make_sure_directory_exists('header', os.path.dirname(headerFilename))
113 | if '$ref' not in schema:
114 | srcFileName = "{}.cpp".format(self.resolver.cpp_get_filename_base(path))
115 | self._make_sure_directory_exists('src', os.path.dirname(srcFileName))
116 | srcGenerator.render_template(template_name="source.cpp.jinja2",
117 | output_name=srcFileName,
118 | deps=['"{}"'.format(self.resolver.cpp_get_header(path))],
119 | usings=self.resolver.cpp_get_usings(),
120 | ns=self.resolver.cpp_get_namespace(path),
121 | resolver=self.resolver,
122 | **args)
123 | retval[0] = srcFileName
124 | headerGenerator.render_template(template_name="header.hpp.jinja2",
125 | output_name=headerFilename,
126 | ns=self.resolver.cpp_get_namespace(path),
127 | deps=self.GetDeps(args['schema']),
128 | resolver=self.resolver,
129 | filename=headerFilename,
130 | filepath='',
131 | **args)
132 | retval[1] = headerFilename
133 | return tuple(retval)
134 |
135 |
136 | class LibraryGenerator(object):
137 |
138 | def __init__(self, src_output_dir: str, header_output_dir: str, resolver):
139 | self.output_dir = {
140 | "src": src_output_dir,
141 | "header": header_output_dir,
142 | }
143 | self.resolver = resolver
144 |
145 | def _make_sure_directory_exists(self, output_key, dir_path):
146 | d = os.path.join(self.output_dir[output_key], dir_path)
147 | if not os.path.exists(d):
148 | os.makedirs(d)
149 |
150 | def Generate(self):
151 | retval = [None, "exceptions.hpp"]
152 | headerGenerator = templator.CodeTemplator(self.output_dir['header']).add_template_package('jsonschemacodegen.templates.cpp')
153 | headerGenerator.render_template(template_name="exceptions.hpp.jinja2",
154 | output_name="exceptions.hpp",
155 | ns=self.resolver.cpp_get_lib_ns(),
156 | )
157 | return tuple(retval)
158 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/python/primative.py.jinja2:
--------------------------------------------------------------------------------
1 | {%macro ToPythonType(schemaType)-%}
2 | {%-if schemaType == 'string'-%}
3 | str
4 | {%-elif schemaType == 'integer'-%}
5 | int
6 | {%-elif schemaType == 'number'-%}
7 | float
8 | {%-elif schemaType == 'boolean'-%}
9 | bool
10 | {%-endif-%}
11 | {%-endmacro-%}
12 |
13 | {%-macro FormatValue(schemaType, value)%}
14 | {%-if schemaType == 'string'-%}"{{value}}"
15 | {%-elif schemaType == 'boolean'-%}
16 | {%-if value or value == 'true'%}True{%endif-%}
17 | {%-if not value or value == 'false'%}False{%endif-%}
18 | {%-else-%}
19 | {{-value-}}
20 | {%-endif-%}
21 | {%-endmacro%}
22 |
23 | {%-macro ValidateValue(schema, varName)%}
24 | if not isinstance({{varName}}, {{ToPythonType(schema.type)}}):
25 | raise ValueError("Passed value '{}' was not a {{schema.type}}".format({{varName}}))
26 | {%-if schema.format is defined%}
27 | {%-if schema.format.startswith('uint')%}
28 | if {{varName}} < 0:
29 | raise ValueError("Value '{}' is signed but the format is {}".format({{varName}}, "{{schema.format}}"))
30 | {%-if schema.format.endswith('32')%}
31 | if {{varName}} > 0xFFFFFFFF:
32 | raise ValueError("Value '{}' is greater than the max for {}".format({{varName}}, "{{schema.format}}"))
33 | {%-endif%} {#format is uint32#}
34 | {%-if schema.format.endswith('16')%}
35 | if {{varName}} > 0xFFFF:
36 | raise ValueError("Value '{}' is greater than the max for {}".format({{varName}}, "{{schema.format}}"))
37 | {%-endif%} {#format is uint16#}
38 | {%-if schema.format.endswith('8')%}
39 | if {{varName}} > 0xFF:
40 | raise ValueError("Value '{}' is greater than the max for {}".format({{varName}}, "{{schema.format}}"))
41 | {%-endif%} {#format is uint8#}
42 | {%-elif schema.format.startswith('int')%}
43 | {%-if schema.format.endswith('32')%}
44 | if {{varName}} > 0x7FFFFFFF or {{varName}} < (-1*0x800000000):
45 | raise ValueError("Value '{}' is outside the range for {}".format({{varName}}, "{{schema.format}}"))
46 | {%-endif%} {#format is int32#}
47 | {%-if schema.format.endswith('16')%}
48 | if {{varName}} > 0x7FFF or {{varName}} < (-1*0x80000):
49 | raise ValueError("Value '{}' is outside the range for {}".format({{varName}}, "{{schema.format}}"))
50 | {%-endif%} {#format is int16#}
51 | {%-if schema.format.endswith('8')%}
52 | if {{varName}} > 0x7F or {{varName}} < (-1*0x800):
53 | raise ValueError("Value '{}' is outside the range for {}".format({{varName}}, "{{schema.format}}"))
54 | {%-endif%} {#format is int8#}
55 | {%-endif%} {#format is int/uint#}
56 | {%-endif%} {#format#}
57 | {%-if schema.maxLength is defined%}
58 | if len({{varName}}) > {{schema.maxLength}}:
59 | raise ValueError("Value '{}' length {} is greater than maximum of {{schema.maxLength}}".format({{varName}}, len({{varName}})))
60 | {%-endif%} {#maxLength#}
61 | {%-if schema.minLength is defined%}
62 | if len({{varName}}) < {{schema.minLength}}:
63 | raise ValueError("Value '{}' length {} is less than minimum of {{schema.minLength}}".format({{varName}}, len({{varName}})))
64 | {%-endif%} {#minLength#}
65 | {%-if schema.minimum is defined%}
66 | if {{varName}} < {{schema.minimum}}:
67 | raise ValueError("Value '{}' is less than the minimum of {{schema.minimum}}".format({{varName}}))
68 | {%-endif%} {#minimum#}
69 | {%-if schema.maximum is defined%}
70 | if {{varName}} > {{schema.maximum}}:
71 | raise ValueError("Value '{}' is more than the maximum of {{schema.maximum}}".format({{varName}}))
72 | {%-endif%} {#maximum#}
73 | {%-if schema.exclusiveMinimum is defined%}
74 | if {{varName}} <= {{schema.exclusiveMinimum}}:
75 | raise ValueError("Value '{}' is less than the minimum of {{schema.exclusiveMinimum}}".format({{varName}}))
76 | {%-endif%} {#minimum#}
77 | {%-if schema.exclusiveMaximum is defined%}
78 | if {{varName}} >= {{schema.exclusiveMaximum}}:
79 | raise ValueError("Value '{}' is more than the maximum of {{schema.exclusiveMaximum}}".format({{varName}}))
80 | {%-endif%} {#maximum#}
81 | {%-if schema.multipleOf is defined%}
82 | if ({{varName}} % {{schema.multipleOf}}) != 0:
83 | raise ValueError("Value '{}' is not a multiple of {{schema.multipleOf}}".format({{varName}}))
84 | {%-endif%} {#maximum#}
85 | {%-if schema.enum is defined%}
86 | acceptable_values = [{%for v in schema.enum%}{{FormatValue(schema.type, v)}}, {%endfor%}]
87 | if {{varName}} not in acceptable_values:
88 | raise ValueError("Value '{}' is not in the list of acceptable value: {}".format({{varName}}, acceptable_values))
89 | {%-endif%} {#enum#}
90 | {%-if schema.const is defined%}
91 | if {{varName}} != {{schema.const | quotestring(schema.type)}}:
92 | raise ValueError("Value '{}' is not the constant value '{{schema.const}}'".format({{varName}}))
93 | {%-endif%} {#enum#}
94 | {%-if schema.pattern is defined%}
95 | pattern = r"{{schema.pattern}}"
96 | if re.match(pattern, {{varName}}) is None:
97 | raise ValueError("Value '{}' did not match the established regular expression".format({{varName}}))
98 | {%-endif%}
99 | {%-endmacro-%}
100 |
101 | {%-set fullClassPath -%}
102 | {%-if parentClass is not defined or parentClass is none-%}
103 | {{Name}}
104 | {%-else-%}
105 | {{parentClass}}.{{Name}}
106 | {%-endif-%}
107 | {%-endset-%}
108 |
109 | class {{Name}}(object):
110 | """ This class is a schema-validating wrapper around a {{schema.type}}.
111 | """
112 |
113 | def __init__(self, value{%if schema.default is defined%}={{FormatValue(schema.type, schema.default)}}{%endif%}):
114 | self.Set(value)
115 |
116 | @staticmethod
117 | def _Validate(value):
118 | """Ensures that the provided {{schema.type}} value meets all the schema constraints.
119 | """
120 | {{-ValidateValue(schema, 'value') | indent(8)}}
121 |
122 | def Set(self, new_value) -> {{fullClassPath}}:
123 | if isinstance(new_value, type(self)):
124 | self._value = new_value._value
125 | elif isinstance(new_value, {{ToPythonType(schema.type)}}):
126 | self._Validate(new_value)
127 | self._value = new_value
128 | {%if schema.type == 'number' -%}
129 | elif isinstance(new_value, int):
130 | self._Validate(float(new_value))
131 | self._value = float(new_value)
132 | {%endif-%}
133 | else:
134 | raise TypeError("The provided type was not a {{fullClassPath}} or a {{ToPythonType(schema.type)}}")
135 | return self
136 |
137 | def Get(self) -> {{ToPythonType(schema.type)}}:
138 | return self._value
139 |
140 | def Serializable(self) -> {{ToPythonType(schema.type)}}:
141 | return self.Get()
142 |
143 |
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/numeric.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'constraints.jinja2' as constraint-%}
2 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
3 | {%-if schema.type == 'integer'%}{%-set cpptype = 'int'%}{%-else%}{%-set cpptype = 'double'%}{%-endif-%}
4 | /*! {{Name}} is a wrapper around a {{schema.type}}.
5 | * \brief wrapper around {{cpptype}}
6 | {%-if schema.description %}
7 | * {{schema.description}}
8 | {%-endif%}
9 | */
10 | class {{Name}}
11 | {
12 | public:
13 | {%-if schema.minimum is defined %}
14 | /*! The encapsulated value must not be less than this minimum number.
15 | * This is defined as a static value to encourage static assertations in application code.
16 | */
17 | static constexpr int {{constraint.ExprName('minimum')}} = {{schema.minimum}};
18 | {%endif%}
19 | {%-if schema.maximum is defined %}
20 | /*! The encapsulated value must not exceed this maximum number.
21 | * This is defined as a static value to encourage static assertations in application code.
22 | */
23 | static constexpr int {{constraint.ExprName('maximum')}} = {{schema.maximum}};
24 | {%endif%}
25 | {%-if schema.exclusiveMinimum is defined %}
26 | /*! The encapsulated value must be greater than this minimum number.
27 | * This is defined as a static value to encourage static assertations in application code.
28 | */
29 | static constexpr int {{constraint.ExprName('exclusiveMinimum')}} = {{schema.exclusiveMinimum}};
30 | {%endif%}
31 | {%-if schema.exclusiveMaximum is defined %}
32 | /*! The encapsulated value must be less than this maximum number.
33 | * This is defined as a static value to encourage static assertations in application code.
34 | */
35 | static constexpr int {{constraint.ExprName('exclusiveMaximum')}} = {{schema.exclusiveMaximum}};
36 | {%endif%}
37 | {%-if schema.multipleOf is defined %}
38 | /*! The encapsulated value must be a multiple of {{schema.multipleOf}}.
39 | * This is defined as a static value to encourage static assertations in application code.
40 | */
41 | static constexpr int {{constraint.ExprName('multipleOf')}} = {{schema.multipleOf}};
42 | {%endif%}
43 | {%-if schema.default is defined %}
44 | /*! This is the default value encapsulated by {{Name}}.
45 | * This is defined as a static value to encourage static assertations in application code.
46 | */
47 | static constexpr int {{constraint.ExprName('default')}} = {{schema.default}};
48 | {%endif%}
49 | {%-if schema.const is defined %}
50 | /*! The encapsulated value must only be this value: {{schema.const}}.
51 | * This is defined as a static value to encourage static assertations in application code.
52 | */
53 | static constexpr int {{constraint.ExprName('const')}} = {{schema.const}};
54 | {%endif%}
55 | {%-if schema.const is not defined%}
56 | /*! Constructor
57 | * \param value the initial value
58 | */
59 | {{Name}}({{cpptype}} value);
60 | {%endif%}
61 | {%-if schema.default is defined or schema.const is defined %}
62 | /*! Default value constructor.
63 | * This structure is initialized with the value '{{schema.const or schema.default}}'
64 | */
65 | {{Name}}();
66 | {%-endif%}
67 |
68 | virtual ~{{Name}}() = default;
69 |
70 | /*! Cast to {{cpptype}} operator
71 | */
72 | operator {{cpptype}}() const;
73 |
74 | /*! Stream operator.
75 | */
76 | friend std::ostream& operator<<(std::ostream& os, const {{Name}}& num);
77 |
78 | /*! The less than comparison operator.
79 | * \param left A {{Name}} instance on the left size of the less than expression.
80 | * \param right A {{Name}} instance on the right side of the less than expression.
81 | * \return true if the less than expression is true.
82 | */
83 | friend bool operator< (const {{Name}}& left, const {{Name}}& right);
84 |
85 | /*! Produce a hash value of the object.
86 | * \param num A {{Name}} instance from which to produce a hash.
87 | * \return a hash number using a boost hashing algorithm.
88 | */
89 | friend std::size_t hash_value(const {{Name}}& num);
90 |
91 | /*! Gets the value.
92 | * \fn {{cpptype}} Get() const
93 | * \return {{cpptype}} value
94 | */
95 | {{cpptype}} Get() const;
96 |
97 | /*! Validates that a value meets schema requirements.
98 | * This is used internally anytime a value is passed into the class, including from the constructor.
99 | * \param testValue This value is checked against all schema contraints.
100 | * \throw {{exception}} When the provided value is invalid according to the schema.
101 | */
102 | static void Validate({{cpptype}} testValue);
103 |
104 | /*! \fn {{Name}} FromJson(const rapidjson::Value& json)
105 | * \brief Deserializes a JSON "{{schema.type}}" value into a new instance of {{Name}}.
106 | * \param json is the RapidJSON value which must be of "null" type.
107 | * \throw {{exception}} If the JSON isn't a {{schema.type}}
108 | * \throw {{exception}} If the value doesn't meet schema constraints
109 | * \returns {{Name}}
110 | */
111 | static {{Name}} FromJson(const rapidjson::Value& json);
112 |
113 | /*! \fn {{Name}} FromString(const std::string& str)
114 | * \brief Performs a "lexical cast" on the string to create a new {{Name}} object
115 | * \param str stringified representation of a {{schema.type}}
116 | * \throw {{exception}} If the string cannot be cast or does not JSON-schema validate
117 | * \return {{Name}}
118 | */
119 | static {{Name}} FromString(const std::string& str);
120 |
121 | /*! \fn ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator)
122 | * \brief Sets 'value' to a {{schema.type}}
123 | * \param value is the RapidJSON value which will be modified to contain the serialization
124 | * \param allocator is the top-level RapidJSON document allocator which may be used for allocations
125 | */
126 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
127 |
128 | /*! Sets a string handle associated with this {{Name}} instance.
129 | * This gets called by a parent object after creating an instance that is used for an object's property.
130 | * \param handle is the string name.
131 | */
132 | void SetHandle(const std::string& handle);
133 |
134 | /*! Gets the string handle associated with this {{Name}} instance.
135 | * This is often the property name used in a JSON-object parent.
136 | * It may be empty.
137 | * \returns the handle string
138 | */
139 | std::string GetHandle() const;
140 |
141 | {%if schema.IsReadOnly() or schema.const is defined%}
142 | private:
143 | {%-endif%}
144 | /*! Assignment from {{cpptype}} operator
145 | */
146 | {{Name}}& operator=({{cpptype}} value);
147 |
148 | /*! Sets the {{cpptype}} value.
149 | * \fn void Set({{cpptype}} value)
150 | * \param value new value
151 | */
152 | {{Name}}& Set({{cpptype}} value);
153 | private:
154 | {{cpptype}} _value;
155 | std::string _handle;
156 | };
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/string.hpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset-%}
2 | /*! {{Name}} is a wrapper around a {{schema.type}}.
3 | {%-if schema.description %}
4 | * {{schema.description}}
5 | {%-endif%}
6 | */
7 | class {{Name}}
8 | {
9 | public:
10 | {%-if schema.maxLength is defined %}
11 | /*! The maximum length of the contained string.
12 | * Trying to set a string longer than this length will throw.
13 | * It is useful to use this value in a static_assert statement
14 | * to ensure it is consistant with external requirements.
15 | */
16 | static constexpr int MAX_LENGTH = {{schema.maxLength}};
17 | {%endif%}
18 | {%-if schema.minLength is defined %}
19 | /*! The minimum length of the contained string.
20 | * Trying to set a string shorter than this length will throw.
21 | * It is useful to use this value in a static_assert statement
22 | * to ensure it is consistant with external requirements.
23 | */
24 | static constexpr int MIN_LENGTH = {{schema.minLength}};
25 | {%endif%}
26 | {%-if schema.default is defined or schema.const is defined%}
27 | /*! The default value of the string.
28 | * If the string has not been set to a value, "{{schema.default or schema.const}}" would be returned for value getting methods.
29 | */
30 | static constexpr auto DEFAULT_VALUE = "{{schema.default or schema.const}}";
31 |
32 | {%-endif%}
33 | {%-if schema.const is defined%}
34 | /*! The constant value of the string.
35 | * The value is not allowed to be anything other than "{{schema.const}}".
36 | */
37 | static constexpr auto CONST_VALUE = "{{schema.const}}";
38 | {%-endif%}
39 | {%-if schema.const is not defined%}
40 |
41 | /*! Constructor.
42 | * \param value initial value of {{Name}} instance.
43 | */
44 | {{Name}}(const std::string& value);
45 |
46 | /*! Constructor.
47 | * \param value initial value of {{Name}} instance.
48 | */
49 | {{Name}}(const char* value);
50 | {%-endif%}
51 | {%-if schema.default is defined or schema.const is defined%}
52 |
53 | /*! Constructor that sets the objects value to "{{schema.const or schema.default}}".
54 | */
55 | {{Name}}();
56 | {%-elif schema.format is defined and schema.format == 'uuid'%}
57 |
58 | /*! Constructor that sets the objects value to a new unique UUID.
59 | */
60 | {{Name}}();
61 | {%-endif-%}{# #}
62 |
63 | /*! Default destructor.
64 | */
65 | virtual ~{{Name}}() = default;
66 |
67 | /*! Operator to cast a {{Name}} instance to a string.
68 | */
69 | operator std::string() const;
70 | /*! Provide the string value of the {{Name}} instance to a stream.
71 | * \param os the stream object
72 | * \param str an instance of {{Name}}
73 | * \return the same ostream input object
74 | */
75 | friend std::ostream& operator<<(std::ostream& os, const {{Name}}& str);
76 |
77 | /*! The less than comparison operator.
78 | * \param left A {{Name}} instance on the left size of the less than expression.
79 | * \param right A {{Name}} instance on the right side of the less than expression.
80 | * \return true if the less than expression is true.
81 | */
82 | friend bool operator< (const {{Name}}& left, const {{Name}}& right);
83 |
84 | /*! Produce a hash value of the string.
85 | * \param str A {{Name}} instance from which to produce a hash.
86 | * \return a hash number using a boost hashing algorithm.
87 | */
88 | friend std::size_t hash_value(const {{Name}}& str);
89 |
90 | /*! Get the contained string.
91 | * \return The contained string.
92 | */
93 | std::string Get() const;
94 |
95 | /*! Validate that the provided string meets schema constraints.
96 | * This is used internally anytime a value is passed into the class, including from the constructor.
97 | * \param testValue string for evaluation
98 | * \throw {{exception}} If the string doesn't meet requirements
99 | */
100 | static void Validate(const std::string& testValue);
101 |
102 | /*! Create a new {{Name}} object from a JSON structure.
103 | * \throw {{exception}} If the string didn't meet the schema's constraints.
104 | * \return new {{Name}} object containing values from the JSON.
105 | * \param json JSON structure to use for creating new {{Name}} object.
106 | */
107 | static {{Name}} FromJson(const rapidjson::Value& json);
108 |
109 | /*! Create a new {{Name}} object from the string.
110 | * The string must meet the schema's constraints.
111 | * \return new {{Name}} object containing the string.
112 | * \param str the string that will be contained in the new {{Name}} object.
113 | * \throw {{exception}} If the JSON value isn't a JSON string.
114 | * \throw {{exception}} If the string didn't meet the schema's constraints.
115 | */
116 | static {{Name}} FromString(const std::string& str);
117 |
118 | /*! Serialize the {{Name}} instance into a JSON object.
119 | * \param value is modified to be a string type containing the instance's string value.
120 | * \param allocator is used for rapidjson allocation.
121 | */
122 | void ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const;
123 |
124 | /*! Sets a string handle associated with this {{Name}} instance.
125 | * This gets called by a parent object after creating an instance that is used for an object's property.
126 | * \param handle is the string name.
127 | */
128 | void SetHandle(const std::string& handle);
129 |
130 | /*! Gets the string handle associated with this {{Name}} instance.
131 | * This is often the property name used in a JSON-object parent.
132 | * It may be empty.
133 | * \returns the handle string
134 | */
135 | std::string GetHandle() const;
136 |
137 | {%if schema.IsReadOnly() or schema.const is defined %}
138 | private:
139 | {%-endif%}
140 | /*! Assignment operator to set the value of the {{Name}} instance.
141 | * \param value the new string value.
142 | * \throw {{exception}} if the passed string value doesn't meet schema validation.
143 | * \return reference to the {{Name}} instance
144 | */
145 | {{Name}}& operator=(const std::string& value);
146 |
147 | /*! Assignment operator to set the value of the {{Name}} instance.
148 | * \param value the new string value.
149 | * \throw {{exception}} if the passed string value doesn't meet schema validation.
150 | * \return reference to the {{Name}} instance
151 | */
152 | {{Name}}& operator=(const char* value);
153 |
154 | /*! Set the value of the string.
155 | * \param value the new string value.
156 | * \throw {{exception}} If the passed string value doesn't meet schema validation.
157 | */
158 | {{Name}}& Set(const std::string& value);
159 |
160 | /*! Set the value of the string.
161 | * \param value the new string value.
162 | * \throw {{exception}} If the passed string value doesn't meet schema validation.
163 | */
164 | {{Name}}& Set(const char* value);
165 |
166 | {%-if schema.format is defined and schema.format == 'uuid'%}
167 | /*! Set the encapsulated value of the {{Name}} object to a new UUID.
168 | */
169 | {{Name}}& SetUuid();
170 | {%-endif%}
171 |
172 | private:
173 | std::string _value;
174 | std::string _handle;
175 | };
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/object.cpp.jinja2:
--------------------------------------------------------------------------------
1 | {%-import 'propname.jinja2' as helper-%}
2 | {%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
3 | {%-set rapidjson = resolver.cpp_resolve_namespace(['rapidjson']) %}
4 | {%macro NestedObjectNameOnly(name) -%}
5 | {%-if name|UpperCamelCase != Name|UpperCamelCase%}{{-name | UpperCamelCase-}}{%else%}{{name | UpperCamelCase}}Property{%endif-%}
6 | {%-endmacro%}
7 | {%macro NestedObjectName(name) -%}
8 | {{className}}::{{NestedObjectNameOnly(name)}}
9 | {%-endmacro%}
10 | {%-macro ObjectType(propName, propSchema) -%}
11 | {%-if '$ref' in propSchema -%}
12 | {{-loader.Reference(resolver, propSchema['$ref'])-}}
13 | {%-else-%}
14 | {{-NestedObjectName(propName)-}}
15 | {%-endif-%}
16 | {%-endmacro%}
17 | {%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset-%}
18 |
19 | {%import 'loader.jinja2' as loader with context%}
20 | {%-for propName, propSchema in schema.RequiredList() %}
21 | {{loader.Class('cpp', resolver, [className], NestedObjectNameOnly(propName), propSchema) }}
22 | {%-endfor%}
23 | {%-for propName, propSchema in schema.UnRequiredList() %}
24 | {{loader.Class('cpp', resolver, [className], NestedObjectNameOnly(propName), propSchema) }}
25 | {%-endfor%}
26 |
27 | {%if schema.RequiredList() | length > 0 %}
28 | {{className}}::{{Name}}(
29 | {%-for propName, propSchema in schema.RequiredList()-%}
30 | const {{ObjectType(propName, propSchema)}}& {{propName | camelCase}}{%if not loop.last%}, {%endif%}
31 | {%-endfor%}){{' : '}}
32 | {%-for propName, propSchema in schema.RequiredList()-%}
33 | {{propName|privatize}}({{propName | camelCase}}){%if not loop.last%}, {%endif%}
34 | {%-endfor%}
35 | {
36 |
37 | }
38 | {%else%}
39 | {{className}}::{{Name}}()
40 | {
41 |
42 | }
43 |
44 | {%-if schema['properties'] | length == 1 %}
45 | {%-set propName = schema.PropertyKeys()[0] %}
46 | {%-set propSchema = schema.PropertyValues()[0] %}
47 | {{className}}::{{Name}}(const {{ObjectType(propName, propSchema)}}& {{propName | camelCase}}) : {{propName|privatize}}({{propName | camelCase}})
48 | {
49 |
50 | }
51 | {%-endif%}
52 | {%if 'type' in propSchema and propSchema['type'] in ['boolean', 'integer', 'number'] %}
53 | {{className}}::{{Name}}({{ {"boolean":"bool", "integer":"int", "number":"double"}[propSchema.type] }} {{propName | camelCase}}) : {{Name}}({{ObjectType(propName, propSchema)}}({{propName | camelCase}}))
54 | {
55 |
56 | }
57 | {%-elif 'type' in propSchema and propSchema['type'] == 'string'-%}
58 | {{className}}::{{Name}}(const std::string& {{propName | camelCase}}) : {{Name}}({{ObjectType(propName, propSchema)}}::FromString({{propName | camelCase}}))
59 | {
60 |
61 | }
62 | {%-endif%} {# propschema type #}
63 | {%endif%}
64 |
65 | {%-for propName, propSchema in schema.properties.items() %}
66 | {%-if propName in schema.required%}
67 |
68 | {{ObjectType(propName, propSchema)}} {{className}}::Get{{propName | UpperCamelCase}}() const
69 | {
70 | return {{propName | privatize}};
71 | }
72 |
73 | {{className}}& {{className}}::Set{{propName | UpperCamelCase}}(const {{ObjectType(propName, propSchema)}}& value)
74 | {
75 | {{propName | privatize}} = value;
76 | {{propName | privatize}}.SetHandle({{helper.ConstPropertyName(propName)}});
77 | return *this;
78 | }
79 | {%else%}
80 | boost::optional<{{ObjectType(propName, propSchema)}}> {{className}}::Get{{propName | UpperCamelCase}}() const
81 | {
82 | return {{propName | privatize}};
83 | }
84 |
85 | {{className}}& {{className}}::Set{{propName | UpperCamelCase}}(const {{ObjectType(propName, propSchema)}}& value)
86 | {
87 | {{propName | privatize}} = value;
88 | {{propName | privatize}}->SetHandle({{helper.ConstPropertyName(propName)}});
89 | return *this;
90 | }
91 | {%endif%}
92 | {%-endfor%}
93 |
94 | {{className}} {{className}}::FromJson(const {{rapidjson}}Value& json)
95 | {
96 | if (!(json.IsObject()))
97 | {
98 | throw {{exception}}("JSON wasn't an object");
99 | }
100 | {##}
101 | {{exception}}Collection exceptionCollection;
102 | {%for propName, propSchema in schema.RequiredList()-%}
103 | boost::optional<{{ObjectType(propName, propSchema)}}> optLocal{{propName | UpperCamelCase}};
104 | if (!json.HasMember({{helper.ConstPropertyName(propName)}}))
105 | {
106 | exceptionCollection.AddException({{exception}}("Property is missing", "{{propName}}"));
107 | }
108 | else
109 | {
110 | optLocal{{propName | UpperCamelCase}} = {{ObjectType(propName, propSchema)}}::FromJson(json[{{helper.ConstPropertyName(propName)}}]);
111 | optLocal{{propName | UpperCamelCase}}->SetHandle({{helper.ConstPropertyName(propName)}});
112 | }
113 | {%endfor%}
114 | boost::optional<{{className}}> optNewInstance;
115 | {%-if schema.RequiredList() %}
116 | if ({%for propName, propSchema in schema.RequiredList()%}optLocal{{propName | UpperCamelCase}}{%if not loop.last%} && {%endif%}{%endfor%})
117 | {
118 | optNewInstance = {{className}}({%for propName, propSchema in schema.RequiredList()%}*optLocal{{propName | UpperCamelCase}}{%if not loop.last%}, {%endif%}{%endfor%});
119 | }
120 | {%else%}
121 | // Yes I know this looks weird to create a boost::optional only to immediately instantiate it.
122 | // However, this is generated code, and there is a branch you don't see here where we conditionally instantiate it.
123 | optNewInstance = {{className}}();
124 | {%-endif%}
125 | {%-for propName, propSchema in schema.UnRequiredList()%}
126 | if (json.HasMember({{helper.ConstPropertyName(propName)}}))
127 | {
128 | try
129 | {
130 | auto local{{propName | UpperCamelCase}} = {{ObjectType(propName, propSchema)}}::FromJson(json[{{helper.ConstPropertyName(propName)}}]);
131 | local{{propName | UpperCamelCase}}.SetHandle({{helper.ConstPropertyName(propName)}});
132 | if (optNewInstance)
133 | {
134 | optNewInstance->Set{{propName | UpperCamelCase}}(local{{propName | UpperCamelCase}});
135 | }
136 | }
137 | catch (const {{exception}}& e)
138 | {
139 | exceptionCollection.AddException(e, {{helper.ConstPropertyName(propName)}});
140 | }
141 | catch (const {{exception}}Collection& ec)
142 | {
143 | exceptionCollection.AddException(ec, {{helper.ConstPropertyName(propName)}});
144 | }
145 | catch (const std::exception& e)
146 | {
147 | exceptionCollection.AddException({{exception}}(e), {{helper.ConstPropertyName(propName)}});
148 | }
149 | }
150 | {%-endfor%}
151 | if (exceptionCollection.IsExceptional() || !optNewInstance)
152 | {
153 | throw exceptionCollection;
154 | }
155 | return *optNewInstance;
156 | }
157 |
158 | void {{className}}::ToJson({{rapidjson}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
159 | {
160 | if (!value.IsObject())
161 | {
162 | value.SetObject();
163 | }
164 |
165 | {%-for propName, propSchema in schema.properties.items() %}
166 | {%-if propName in schema.required%}
167 | {{rapidjson}}Value temp{{propName|UpperCamelCase}};
168 | {{propName|privatize}}.ToJson(temp{{propName|UpperCamelCase}}, allocator);
169 | value.AddMember({{rapidjson}}StringRef({{helper.ConstPropertyName(propName)}}), temp{{propName|UpperCamelCase}}, allocator);
170 | {%else%}
171 | if ({{propName|privatize}})
172 | {
173 | {{rapidjson}}Value temp{{propName|UpperCamelCase}};
174 | {{propName|privatize}}->ToJson(temp{{propName|UpperCamelCase}}, allocator);
175 | value.AddMember({{rapidjson}}StringRef({{helper.ConstPropertyName(propName)}}), temp{{propName|UpperCamelCase}}, allocator);
176 | }
177 | {%endif%}
178 | {%-endfor%}
179 | }
180 |
181 | void {{className}}::SetHandle(const std::string& handle)
182 | {
183 | _handle = handle;
184 | }
185 |
186 | std::string {{className}}::GetHandle() const
187 | {
188 | return _handle;
189 | }
--------------------------------------------------------------------------------
/jsonschemacodegen/templates/cpp/exceptions.hpp.jinja2:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | {%for n in ns-%}
10 | namespace {{n}} {
11 | {%-endfor%}
12 |
13 | /*! Encapsulates a JSON path. This is not the "JSONPath" standard, but a simple representation of a pointer to a value in a JSON-object tree.
14 | * For example, the path `A/B/C` would point to _"this is the string"_ in the following JSON object:
15 | * ```
16 | {
17 | "A" : {
18 | "B": {
19 | "C": "this is the string",
20 | "X0": 1
21 | },
22 | "X1": null
23 | },
24 | "X2": false
25 | }
26 | ```
27 | */
28 | class JsonPath
29 | {
30 | public:
31 | /*! Empty constructor.
32 | */
33 | JsonPath() : _pathParts({}) {}
34 |
35 | /*! Constructor with a single element in the path.
36 | * \param propName A single element in the path
37 | */
38 | JsonPath(const std::string& propName) : _pathParts({propName}) {}
39 |
40 | /*! Copy constructor.
41 | * \param other Another JsonPath object from which to copy the path.
42 | */
43 | JsonPath(const JsonPath& other) : _pathParts(other._pathParts) {}
44 |
45 | /*! Prepends an element to the beginning of the JSON path.
46 | * \param propName A property name to prepend to the stored path.
47 | * \returns Reference to this JsonPath object (useful for chaining commands).
48 | */
49 | JsonPath& PrependToPath(const std::string& propName)
50 | {
51 | _pathParts.push_back(propName);
52 | return *this;
53 | }
54 |
55 | /*!
56 | * Gets a JSON path representation as strings in a vector. For example, if the path is `A/B/C` then this would return a vector with the elements `{"A", "B", "C"}`.
57 | * \returns A vector of strings representing the path.
58 | */
59 | std::vector GetPath() const
60 | {
61 | std::vector ret;
62 | // Parts are stored in reverse order.
63 | ret.insert(ret.end(), _pathParts.crbegin(), _pathParts.crend());
64 | return ret;
65 | }
66 |
67 | /*!
68 | * Returns a string representation of a JSON path. For example: `Highest/Middle/Leaf`.
69 | * \returns String representation of the path.
70 | */
71 | std::string PathAsString() const
72 | {
73 | return boost::algorithm::join(GetPath(), "/");
74 | }
75 |
76 | private:
77 | /*! In this private member variable, the parts are stored in reverse order.
78 | */
79 | std::vector _pathParts;
80 | };
81 |
82 | /*! This exception type should be used when there is any problem with a JSON document.
83 | */
84 | class JsonException : public std::exception
85 | {
86 |
87 | };
88 |
89 | /*! This exception type should be used when rapidjson couldn't parse a JSON document.
90 | */
91 | class RapidJsonParseException : public JsonException
92 | {
93 | public:
94 | /*! Constructor (C strings).
95 | * @param whatArg C-style string error message.
96 | */
97 | explicit RapidJsonParseException(const char* whatArg) : JsonException(), _whatArg(whatArg) { }
98 |
99 | /*! Constructor (C++ STL strings).
100 | * @param whatArg The error message.
101 | */
102 | explicit RapidJsonParseException(const std::string& whatArg) : JsonException(), _whatArg(whatArg) { }
103 |
104 | /** Destructor.
105 | * Virtual to allow for subclassing.
106 | */
107 | virtual ~RapidJsonParseException() throw () {}
108 |
109 | /** Returns a pointer to the (constant) error description.
110 | * @return A pointer to a const char*.
111 | */
112 | virtual const char* what() const throw()
113 | {
114 | return _whatArg.c_str();
115 | }
116 |
117 | protected:
118 | std::string _whatArg;
119 | };
120 |
121 | /*! This exception type should be used for exceptions that are invalid according to a JSON schema.
122 | * It is meant to be a parent class for more specific schema exceptions to inherit from.
123 | */
124 | class SchemaValidationException : public JsonException
125 | {
126 |
127 | };
128 |
129 | /*! This class of exception handles any type of schema validation errors.
130 | * In additional to the error message, it also keeps the JSON path to describe the JSON-tree location of the error.
131 | */
132 | class JsonSchemaException : public SchemaValidationException
133 | {
134 | public:
135 |
136 | /*! Simple constructor that looks like the constructor for most exceptions.
137 | * \param whatArg The message that should be include in the message returned by the `what()` method.
138 | */
139 | JsonSchemaException(const std::string& whatArg) : SchemaValidationException(), _whatVal(""), _whatArg(whatArg), _jsonPath()
140 | {
141 |
142 | }
143 |
144 | /*! Constructor that also takes a JSON-object property name.
145 | * \param whatArg The message that should be include in the message returned by the `what()` method.
146 | * \param propName The name of the JSON-object property that failed JSON Schema validation.
147 | */
148 | JsonSchemaException(const std::string& whatArg, const std::string& propName) : SchemaValidationException(), _whatVal(""), _whatArg(whatArg), _jsonPath(propName)
149 | {
150 |
151 | }
152 |
153 | /*! Constructor that accepts an existing JSON path.
154 | * \param whatArg The message that should be include in the message returned by the `what()` method.
155 | * \param jsonPath An existing JSON path representation object.
156 | */
157 | JsonSchemaException(const std::string& whatArg, const JsonPath& jsonPath) : SchemaValidationException(), _whatVal(""), _whatArg(whatArg), _jsonPath(jsonPath)
158 | {
159 |
160 | }
161 |
162 | /*! Constructor that assumes the message of an existing std::exception.
163 | * \param other An existing exception from which to copy the exception message.
164 | */
165 | explicit JsonSchemaException(const std::exception& other): SchemaValidationException(), _whatVal(""), _whatArg(other.what()), _jsonPath()
166 | {
167 |
168 | }
169 |
170 | /*! Adds to the front of the contained JSON path.
171 | * When an error is found in recursively parsing a JSON object tree, additional parts of the JSON path are prepended as the exception bubbles up through the recursive layers.
172 | * \param propName The path string to add to the front of the path.
173 | */
174 | void PrependToPath(const std::string& propName)
175 | {
176 | _jsonPath.PrependToPath(propName);
177 | }
178 |
179 | /*! Returns the error message, prepended by a Json Path string pointing to where the exception was found.
180 | * \returns The exception's error message.
181 | */
182 | virtual const char* what() const throw()
183 | {
184 | std::stringstream ss;
185 | ss << _jsonPath.PathAsString();
186 | ss << ": ";
187 | ss << _whatArg;
188 | _whatVal = ss.str();
189 | return _whatVal.c_str();
190 | }
191 |
192 | /*! Gets the Json Path associated with the exception.
193 | * \returns The exception's Json Path.
194 | */
195 | JsonPath WhatJsonPath() const
196 | {
197 | return _jsonPath;
198 | }
199 |
200 | /*! Gets only the exception message (without any Json path info).
201 | * \returns The exception's error string.
202 | */
203 | std::string WhatMessage() const
204 | {
205 | return _whatArg;
206 | }
207 |
208 | private:
209 | mutable std::string _whatVal;
210 | std::string _whatArg;
211 | JsonPath _jsonPath;
212 | };
213 |
214 |
215 | /*! This exception class can contain information from other exceptions.
216 | */
217 | class JsonSchemaExceptionCollection : public SchemaValidationException
218 | {
219 | public:
220 |
221 | /*! Empty constructor.
222 | * This can be created before any exceptions are thrown, so that when any exceptions _are_ thrown, they can be added to this class.
223 | */
224 | JsonSchemaExceptionCollection() : SchemaValidationException(), _whatVal(""), _subExceptions({}) { }
225 |
226 | /*! Default destructor.
227 | */
228 | virtual ~JsonSchemaExceptionCollection() = default;
229 |
230 | /*! Adds an exception to the collection.
231 | * \param exc The exception to add to the collection.
232 | */
233 | void AddException(const JsonSchemaException& exc)
234 | {
235 | _subExceptions.push_back(exc);
236 | }
237 |
238 | /*! Adds an exception to the collection, setting the Json path associated with the exception.
239 | * \param exc The exception to add to the collection.
240 | * \param propName This is the name of the property from which the exception was thrown.
241 | */
242 | void AddException(const JsonSchemaException& exc, const std::string& propName)
243 | {
244 | _subExceptions.push_back(exc);
245 | _subExceptions.back().PrependToPath(propName);
246 | }
247 |
248 | /*! Adds all the exceptions from a different JsonSchemaExceptionCollection to this collection.
249 | * \param exc A different exception collection from which to copy exceptions.
250 | */
251 | void AddException(const JsonSchemaExceptionCollection& exc)
252 | {
253 | auto exceptions = exc.GetExceptions();
254 | _subExceptions.insert(_subExceptions.end(), exceptions.begin(), exceptions.end());
255 | }
256 |
257 | /*! Adds all the exceptions from a different JsonSchemaExceptionCollection to this collection.
258 | * \param exc A different exception collection from which to copy exceptions.
259 | * \param propName This is the name of the property from which the exception was thrown
260 | */
261 | void AddException(const JsonSchemaExceptionCollection& exc, const std::string& propName)
262 | {
263 | std::vector exceptions = exc.GetExceptions();
264 | for (JsonSchemaException iter : exceptions)
265 | {
266 | iter.PrependToPath(propName);
267 | _subExceptions.push_back(iter);
268 | }
269 | }
270 |
271 | /*! Adds any exception to the collection.
272 | * \param exc The exception to add to the collection.
273 | */
274 | void AddException(const std::exception& exc)
275 | {
276 | _subExceptions.push_back(JsonSchemaException(exc));
277 | }
278 |
279 | /*! Adds any exception to the collection.
280 | * \param exc The exception to add to the collection.
281 | * \param propName This is the name of the property from which the exception was thrown.
282 | */
283 | void AddException(const std::exception& exc, const std::string& propName)
284 | {
285 | _subExceptions.push_back(JsonSchemaException(exc));
286 | }
287 |
288 | /*! Prepends an additional layer to the JSON path of each contained object.
289 | * When a JSON object is being recursively parsed, this is needed to add to the JSON paths as the exceptions bubble upward.
290 | * \param propName This name will get prepended to each contained exception's JSON path.
291 | */
292 | void PrependToPath(const std::string& propName)
293 | {
294 | for (auto& iter : _subExceptions)
295 | {
296 | iter.PrependToPath(propName);
297 | }
298 | }
299 |
300 | /*! Indicates if there are any collection exceptions.
301 | * \return true if one or more exceptions have been collected.
302 | */
303 | bool IsExceptional() const {
304 | return !_subExceptions.empty();
305 | }
306 |
307 | /*! Returns the error message.
308 | * \returns The exception's error message.
309 | */
310 | virtual const char* what() const throw()
311 | {
312 | if (!IsExceptional())
313 | {
314 | return "";
315 | }
316 | _whatVal = "";
317 | for (auto& iter : _subExceptions)
318 | {
319 | if (!_whatVal.empty())
320 | {
321 | _whatVal += " ; ";
322 | }
323 | _whatVal += iter.what();
324 | }
325 | return _whatVal.c_str();
326 | }
327 |
328 | /*! Get the collection of exceptions.
329 | * \returns Collection of exceptions.
330 | */
331 | std::vector GetExceptions() const
332 | {
333 | return _subExceptions;
334 | }
335 |
336 | private:
337 | mutable std::string _whatVal;
338 | std::vector _subExceptions;
339 | };
340 |
341 | {%for n in ns-%}
342 | } //end namespace {{n}}
343 | {%-endfor%}
344 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
--------------------------------------------------------------------------------