GithubHelp home page GithubHelp logo

python-jsonschema-objects's Introduction

Build

What

python-jsonschema-objects provides an automatic class-based binding to JSON Schemas for use in python. See Draft Schema Support to see supported keywords

For example, given the following schema:

{
    "title": "Example Schema",
    "type": "object",
    "properties": {
        "firstName": {
            "type": "string"
        },
        "lastName": {
            "type": "string"
        },
        "age": {
            "description": "Age in years",
            "type": "integer",
            "minimum": 0
        },
        "dogs": {
            "type": "array",
            "items": {"type": "string"},
            "maxItems": 4
        },
        "address": {
            "type": "object",
            "properties": {
                "street": {"type": "string"},
                "city": {"type": "string"},
                "state": {"type": "string"}
                },
            "required":["street", "city"]
            },
        "gender": {
            "type": "string",
            "enum": ["male", "female"]
        },
        "deceased": {
            "enum": ["yes", "no", 1, 0, "true", "false"]
            }
    },
    "required": ["firstName", "lastName"]
}

jsonschema-objects can generate a class based binding. Assume here that the schema above has been loaded in a variable called examples:

>>> import python_jsonschema_objects as pjs
>>> builder = pjs.ObjectBuilder(examples['Example Schema'])
>>> ns = builder.build_classes()
>>> Person = ns.ExampleSchema
>>> james = Person(firstName="James", lastName="Bond")
>>> james.lastName
<Literal<str> Bond>
>>> james.lastName == "Bond"
True
>>> james
<example_schema address=None age=None deceased=None dogs=None firstName=<Literal<str> James> gender=None lastName=<Literal<str> Bond>>

Validations will also be applied as the object is manipulated.

>>> james.age = -2  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...
ValidationError: -2 is less than 0

>>> james.dogs= ["Jasper", "Spot", "Noodles", "Fido", "Dumbo"]  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...
ValidationError: ["Jasper", "Spot", "Noodles", "Fido", "Dumbo"]  has too many elements. Wanted 4.

The object can be serialized out to JSON. Options are passed through to the standard library JSONEncoder object.

>>> james.serialize(sort_keys=True)
'{"firstName": "James", "lastName": "Bond"}'

Why

Ever struggled with how to define message formats? Been frustrated by the difficulty of keeping documentation and message definition in lockstep? Me too.

There are lots of tools designed to help define JSON object formats, foremost among them JSON Schema. JSON Schema allows you to define JSON object formats, complete with validations.

However, JSON Schema is language agnostic. It validates encoded JSON directly - using it still requires an object binding in whatever language we use. Often writing the binding is just as tedious as writing the schema itself.

This avoids that problem by auto-generating classes, complete with validation, directly from an input JSON schema. These classes can seamlessly encode back and forth to JSON valid according to the schema.

Fully Functional Literals

Literal values are wrapped when constructed to support validation and other schema-related operations. However, you can still use them just as you would other literals.

>>> import python_jsonschema_objects as pjs
>>> builder = pjs.ObjectBuilder(examples['Example Schema'])
>>> ns = builder.build_classes()
>>> Person = ns.ExampleSchema
>>> james = Person(firstName="James", lastName="Bond")
>>> str(james.lastName)
'Bond'
>>> james.lastName += "ing"
>>> str(james.lastName)
'Bonding'
>>> james.age = 4
>>> james.age - 1
3
>>> 3 + james.age
7
>>> james.lastName / 4
Traceback (most recent call last):
    ...
TypeError: unsupported operand type(s) for /: 'str' and 'int'

Accessing Generated Objects

Sometimes what you really want to do is define a couple of different objects in a schema, and then be able to use them flexibly.

Any object built as a reference can be obtained from the top level namespace. Thus, to obtain multiple top level classes, define them separately in a definitions structure, then simply make the top level schema refer to each of them as a oneOf.

Other classes identified during the build process will also be available from the top level object. However, if you pass named_only to the build_classes call, then only objects with a title will be included in the output namespace.

Finally, by default, the names in the returned namespace are transformed by passing them through a camel case function. If you want to have names unchanged, pass standardize_names=False to the build call.

The schema and code example below show how this works.

{
    "title": "MultipleObjects",
    "id": "foo",
    "type": "object",
    "oneOf":[
            {"$ref": "#/definitions/ErrorResponse"},
            {"$ref": "#/definitions/VersionGetResponse"}
            ],
    "definitions": {
        "ErrorResponse": {
            "title": "Error Response",
            "id": "Error Response",
            "type": "object",
            "properties": {
                "message": {"type": "string"},
                "status": {"type": "integer"}
            },
            "required": ["message", "status"]
        },
        "VersionGetResponse": {
            "title": "Version Get Response",
            "type": "object",
            "properties": {
                "local": {"type": "boolean"},
                "version": {"type": "string"}
            },
            "required": ["version"]
        }
    }
}
>>> builder = pjs.ObjectBuilder(examples["MultipleObjects"])
>>> classes = builder.build_classes()
>>> [str(x) for x in dir(classes)]
['ErrorResponse', 'Local', 'Message', 'Multipleobjects', 'Status', 'Version', 'VersionGetResponse']
>>> classes = builder.build_classes(named_only=True, standardize_names=False)
>>> [str(x) for x in dir(classes)]
['Error Response', 'MultipleObjects', 'Version Get Response']
>>> classes = builder.build_classes(named_only=True)
>>> [str(x) for x in dir(classes)]
['ErrorResponse', 'Multipleobjects', 'VersionGetResponse']

Supported Operators

$ref

The $ref operator is supported in nearly all locations, and dispatches the actual reference resolution to the referencing.Registry resolver.

This example shows using the memory URI (described in more detail below) to create a wrapper object that is just a string literal.

{
    "title": "Just a Reference",
    "$ref": "memory:Address"
}
>>> builder = pjs.ObjectBuilder(examples['Just a Reference'], resolved=examples)
>>> ns = builder.build_classes()
>>> ns.JustAReference('Hello')
<Literal<str> Hello>

Circular References

Circular references are not a good idea, but they're supported anyway via lazy loading (as much as humanly possible).

Given the crazy schema below, we can actually generate these classes.

{
    "title": "Circular References",
    "id": "foo",
    "type": "object",
    "oneOf":[
            {"$ref": "#/definitions/A"},
            {"$ref": "#/definitions/B"}
    ],
    "definitions": {
        "A": {
            "type": "object",
            "properties": {
                "message": {"type": "string"},
                "reference": {"$ref": "#/definitions/B"}
            },
            "required": ["message"]
        },
        "B": {
            "type": "object",
            "properties": {
                "author": {"type": "string"},
                "oreference": {"$ref": "#/definitions/A"}
            },
            "required": ["author"]
        }
    }
}

We can instantiate objects that refer to each other.

>>> builder = pjs.ObjectBuilder(examples['Circular References'])
>>> klasses = builder.build_classes()
>>> a = klasses.A()
>>> b = klasses.B()
>>> a.message= 'foo'
>>> a.reference = b  # doctest: +IGNORE_EXCEPTION_DETAIL 
Traceback (most recent call last):
    ...
ValidationError: '[u'author']' are required attributes for B
>>> b.author = "James Dean"
>>> a.reference = b
>>> a
<A message=<Literal<str> foo> reference=<B author=<Literal<str> James Dean> oreference=None>>

The "memory:" URI

"memory:" URIs are deprecated (although they still work). Load resources into a referencing.Registry instead and pass those in

The ObjectBuilder can be passed a dictionary specifying 'memory' schemas when instantiated. This will allow it to resolve references where the referenced schemas are retrieved out of band and provided at instantiation.

For instance, given the following schemas:

{
    "title": "Address",
    "type": "string"
}
{
    "title": "AddlPropsAllowed",
    "type": "object",
    "additionalProperties": true
}
{
    "title": "Other",
    "type": "object",
    "properties": {
        "MyAddress": {"$ref": "memory:Address"}
    },
    "additionalProperties": false
}

The ObjectBuilder can be used to build the "Other" object by passing in a definition for "Address".

>>> builder = pjs.ObjectBuilder(examples['Other'], resolved={"Address": {"type":"string"}})
>>> builder.validate({"MyAddress": '1234'})
>>> ns = builder.build_classes()
>>> thing = ns.Other()
>>> thing
<other MyAddress=None>
>>> thing.MyAddress = "Franklin Square"
>>> thing
<other MyAddress=<Literal<str> Franklin Square>>
>>> thing.MyAddress = 423  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...
ValidationError: 432 is not a string

oneOf

Generated wrappers can properly deserialize data representing 'oneOf' relationships, so long as the candidate schemas are unique.

{
    "title": "Age",
    "type": "integer"
}

{
    "title": "OneOf",
    "type": "object",
    "properties": {
        "MyData": { "oneOf":[
            {"$ref": "memory:Address"},
            {"$ref": "memory:Age"}
            ]
        }
    },
    "additionalProperties": false
}
{
    "title": "OneOfBare",
    "type": "object",
    "oneOf":[
            {"$ref": "memory:Other"},
            {"$ref": "memory:Example Schema"}
            ],
    "additionalProperties": false
}

Installation

pip install python_jsonschema_objects

Tests

Tests are managed using the excellent Tox. Simply pip install tox, then tox.

Draft Keyword Support

Most of draft-4 is supported, so only exceptions are noted in the table. Where a keyword functionality changed between drafts, the version that is supported is noted.

The library will warn (but not throw an exception) if you give it an unsupported $schema

Keyword supported version
$id true draft-6
propertyNames false
contains false
const false
required true draft-4
examples false
format false

Changelog

Please refer to Github releases for up to date changelogs.

0.0.18

  • Fix assignment to schemas defined using 'oneOf'
  • Add sphinx documentation and support for readthedocs

0.0.16 - Fix behavior of exclusiveMinimum and exclusiveMaximum validators so that they work properly.

0.0.14 - Roll in a number of fixes from Github contributors, including fixes for oneOf handling, array validation, and Python 3 support.

0.0.13 - Lazily build object classes. Allows low-overhead use of jsonschema validators.

0.0.12 - Support "true" as a value for 'additionalProperties'

0.0.11 - Generated wrappers can now properly deserialize data representing 'oneOf' relationships, so long as the candidate schemas are unique.

0.0.10 - Fixed incorrect checking of enumerations which previously enforced that all enumeration values be of the same type.

0.0.9 - Added support for 'memory:' schema URIs, which can be used to reference externally resolved schemas.

0.0.8 - Fixed bugs that occurred when the same class was read from different locations in the schema, and thus had a different URI

0.0.7 - Required properties containing the '@' symbol no longer cause build_classes() to fail.

0.0.6 - All literals now use a standardized LiteralValue type. Array validation actually coerces element types. as_dict can translate objects to dictionaries seamlessly.

0.0.5 - Improved validation for additionalItems (and tests to match). Provided dictionary-syntax access to object properties and iteration over properties.

0.0.4 - Fixed some bugs that only showed up under specific schema layouts, including one which forced remote lookups for schema-local references.

0.0.3b - Fixed ReStructuredText generation

0.0.3 - Added support for other array validations (minItems, maxItems, uniqueItems).

0.0.2 - Array item type validation now works. Specifying 'items', will now enforce types, both in the tuple and list syntaxes.

0.0.1 - Class generation works, including 'oneOf' and 'allOf' relationships. All basic validations work.

python-jsonschema-objects's People

Contributors

allista avatar arthurvanduynhoven avatar azubieta avatar cwacek avatar davidbargeron avatar dependabot[bot] avatar dohrayme avatar eliahkagan avatar farshidce avatar ferringb avatar fitblip avatar fmigneault avatar itielshwartz avatar jar3b avatar jepperaskdk avatar jonkohler avatar kennydo avatar mineo avatar nkakouros avatar othermark avatar reece avatar replay avatar rmspeers avatar rolandjitsu avatar romancedric avatar salbertson avatar schuyler-glympse avatar sergio-alonso avatar the-alchemist avatar thinkjd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-jsonschema-objects's Issues

Top-level array elements are not built into objects

Here is a working example of an internal array of objects:

example1.json:

{
  "$schema": "http://json-schema.org/draft-04/schema",
  "title": "Example1",
  "type": "object",
  "properties": {
    "name": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {"type": "string"}
        }
      }
    }
  }
}
ns1 = pjs.ObjectBuilder("example1.json").build_classes()
j1 = ns1.Example1.from_json(json.dumps({'name': [{'value':'foo'}, {'value':'bar'}]}))
j1.name[0]  # Out[164]: <name_<anonymous_field> name=foo>
j1.name[0].value  # 'foo'

However a top-level array causes a problem because the array elements do not seem to be "objectified" but remain as plain dicts:

example2.json

{
  "$schema": "http://json-schema.org/draft-04/schema",
  "title": "Example2",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "name": { "type": "string" }
    }
  }
}
ns2 = pjs.ObjectBuilder("example2.json").build_classes()
j2 = ns2.Example2.from_json(json.dumps([{'name': 'foo'}, {'name': 'bar'}]))
j2[0]  # Out[173]: {'name': 'foo'}
type(j2[0])  # Out[179]: dict
j2[0].name  # AttributeError: 'dict' object has no attribute 'name'

pjs._version.get_versions()

{'dirty': False,
 'error': None,
 'full-revisionid': '71a6ad0becfb0b2bc2447015ec6ce90d7e3dc725',
 'version': '0.2.2'}

Request: example for multiple top-level objects in a schema

The readme suggests that multiple objects can be extracted from a schema by ObjectBuilder.build_classes, however it's really not clear to me how one can define multiple objects within a single schema in a way where this would work. Can you please give an example of how you see this working?

For example, given a schema that (somehow) contains the definition of two top-level objects, the expectation might be that the result of ObjectBuilder(schema).build_classes() is a namespace with classes for both objects. Does this require combination of schemas with the anyOf directive?

Or is the intention that there would be a separate schema for each object, and therefore ObjectBuilder(schema).build_classes() would need to be called within a loop over all schemas?

Example:

schema = {
    "id": "My Schema",
    "properties": {
        "ErrorResponse": {
            "title": "Error Response",
            "type": "object",
            "properties": {
                "message": {"type": "string"},
                "status": {"type": "integer"},
            },
            "required": ["message", "status"],
        },
        "VersionGetResponse": {
            "title": "Version Get Response",
            "type": "object",
            "properties": {
                "local": {"type": "boolean"},
                "version": {"type": "string"},
            },
            "required": ["version"],
        }
    }
}

print(dir(pjs.ObjectBuilder(schema).build_classes()))
# Out: ['ErrorResponse<anonymous>', 'Local', 'Message', 'Status', 'MySchema', 'Version', 'VersionGetResponse<anonymous>']

In this case, the two useful classes have <anonymous> suffixes which makes them difficult (but not impossible) to use.

facing an issue when running the library with logging set as DEBUG

Traceback (most recent call last):
File "/Users/farshid/flab/careabout/schema/functests/testgenerated.py", line 141, in test_WebResponse
obj = Factory.WebResponse()
File "/Users/farshid/flab/careabout/schema/src/careaboutschema/generated/factory.py", line 142, in WebResponse
clazz = load_classes('webresponse.001.json').WebResponse
File "/Users/farshid/flab/careabout/schema/src/careaboutschema/util.py", line 37, in load_classes
loaded_classes[filename] = builder.build_classes()
File "/Users/farshid/flab/careabout/schema/venv/lib/python3.5/site-packages/python_jsonschema_objects/init.py", line 83, in build_classes
builder.construct(nm, self.schema)
File "/Users/farshid/flab/careabout/schema/venv/lib/python3.5/site-packages/python_jsonschema_objects/classbuilder.py", line 388, in construct
logger.debug(util.lazy_format("Constructing {0}", uri))
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 1267, in debug
self._log(DEBUG, msg, args, **kwargs)
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 1415, in _log
self.handle(record)
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 1425, in handle
self.callHandlers(record)
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 1487, in callHandlers
hdlr.handle(record)
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 855, in handle
self.emit(record)
File "/Users/farshid/flab/careabout/schema/venv/lib/python3.5/site-packages/nose/plugins/logcapture.py", line 82, in emit
self.buffer.append(self.format(record))
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 830, in format
return fmt.format(record)
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 567, in format
record.message = record.getMessage()
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/logging/init.py", line 328, in getMessage
msg = str(self.msg)
TypeError: str returned non-string (type NoneType)

i was able to fix this by adding a return to this line to the lazy_format class

14 def str(self):
15 return self.fmt.format(_self.args, *_self.kwargs)

will send a patch shortly

Redfish schema allows special chars in "title" field.

The schema which models a computer system collection (http://redfish.dmtf.org/schemas/v1/ComputerSystemCollection.json) allows for special characters (# and .) in the titlefield.

Does python-jsonschema-objects allow/or support special chars in title field ?

The above schema has the following title field:
"title": "**#ComputerSystemCollection.**ComputerSystemCollection",

Here is the code snippet in which I removed the special chars for referring the namespace object:

with open('ComputerSystemCollection.json') as schema_file:
root_schema = json.load(schema_file)

builder = pjso.ObjectBuilder (root_schema)
ns = builder.build_classes()
csc = ns.ComputerSystemCollectionComputerSystemCollection

Is this the correct access syntax ?

Nested ref's doesn't seem to work.

If I have nested refs, the geterated class object doesn't seem correct. The following should not vaildate ('foo' is not a valid string according to the schema).

classes = python_jsonschema_objects.ObjectBuilder({
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "nested refs",
"description": "test",
"type": "object",
"properties": {
"test2": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/definitions/test3"
}
}
},
"definitions": {
"test3": {
"$ref": "#/definitions/test4"
},
"test4": {
"enum": [
"foo.test1",
"foo.test2",
"foo.test3"
]
}
}
}).build_classes()
nest = classes.NestedRefs(test2=['foo'])

If I instead change it to only one level of refs, it works:

classes = python_jsonschema_objects.ObjectBuilder({
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "nested refs",
"description": "test",
"type": "object",
"properties": {
"test2": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/definitions/test4"
}
}
},
"definitions": {
"test3": {
"$ref": "#/definitions/test4"
},
"test4": {
"enum": [
"foo.test1",
"foo.test2",
"foo.test3"
]
}
}
}).build_classes()
nest = classes.NestedRefs(test2=['foo'])

Update requirements

I'm having some issues with installing this package alongside https://github.com/jupyter-widgets/ipywidgets and jupyter notebooks as they depend on jsonschema>=2.4 (or another one of their deps depends on it) whereas this package is fixed at v2.3.0.

Could the requirements for this package be a bit more flexible? It also applies for pandocfilters package.

Full support for Draft4 of the schema

Hi guys,

It's a great implementation for JSON Schemas in Python, but I would like to know if you intend to implement the full support for Draft4 or if it's possible to do this considering the complexity of generate class instances with the new keywords of the Draft4.

Thanks!

Ref problem

I am trying to get this nice python ui for json files (using json schema) to work, but I ran into some problems. I am trying to understand your code but it will take a while, so I am hoping for a quick answer / solution by posting this issue here.

I have several files linked like this:
Person.json
{
"$ref": "Person/PersonV1.json"
}

The validation using the python jsonschema package works just fine, had to add a resolver for dealing with all the relative path refs.

For your package I used "RefResolutionError" solution, but I same to get a problem af another nature. When running:

import python_jsonschema_objects as pjs
builder = pjs.ObjectBuilder('some_path/Person.json')
builder.build_classes()

I get


KeyError Traceback (most recent call last)
in ()
----> 1 builder.build_classes()

C:\Miniconda2\lib\site-packages\python_jsonschema_objects_init_.py in build_classes(self, strict)
116 builder.construct(uri, defn)
117
--> 118 nm = self.schema['title'] if 'title' in self.schema else self.schema['id']
119 nm = inflection.parameterize(six.text_type(nm), '_')
120

KeyError: 'id'

So are files with only a reference not allowed (yet)?

patternProperties

The "patternProperties" keyword can be used to match property names to schemas using regular expressions. Could this be supported by jsonschema-objects? I suppose it might be an obscure feature but it's very useful in my use case.

UnicodeDecodeError on Windows with non-UTF8 preferred encoding

Hello.
I use Python 3.5.2 on Windows 10 (with non-English localization).
When I try to load schema from disk (by creating ObjectBuilder() instance) then I have this error:

  File "D:\Soft\Python35\lib\site-packages\python_jsonschema_objects\__init__.py", line 34, in __init__
    self.schema = json.loads(fin.read())
  File "D:\Soft\Python35\lib\encodings\cp1251.py", line 23, in decode
    return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x98 in position 195: character maps to <undefined>

Code fails 'cos Windows has cp1251 as so-called locale preferred encoding but my schema has utf-8.

It's strange why in ObjectBuilder.__init__() used open(uri) for loading file, but in ObjectBuilder.relative_file_resolver() used codecs.open(path, 'r', 'utf-8') for same action (and usage of second decision is a right way in my opinion at least in my case).
Rewriting open() to codecs.open() in ObjectBuilder constructor solves this issue.

Problem with as_dict() when schema contains properties of type object

Unexpected behavior when calling as_dict on the JSON Schema object if the object contains a property/properties of type "object". Specifically, all other data of the object is serialized out as a dictionary (including arrays of objects) but the object(s) are not serialized properly. Instead resulting in something like "<abc_object attributes: attr1, attr2, attr3,...>".

To better illustrate the problem consider the following schema and expected output from calling as_dict() on the object created from the schema:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "sample",
  "description": "sample description",
  "properties":
  {
    "mystr":
    {
      "description": "string sample",
      "type": "string"
    },
    "myobj":
    {
      "description": "object sample",
      "type": "object",
      "properties":
      {
        "attr1":
        {
          "type": "string"
        },
        "attr2":
        {
          "type": "number"
        }
      }
    }
  }
}

Expected output dictionary:
{"mystr": "test string value", "myobj": {"attr1": "test value", "attr2": 12345}}

Note, that if I modify my schema such that "myobj" is an array whose items are objects produces a dictionary which matches what I would expect as the output value (i.e. "myobj" is an array of objects and the objects are properly serialized within the array).

This can be solved by modifying line 38 in classbuilder.py to be:

out[prop] = propval.as_dict() if hasattr(propval, 'as_dict') else propval

Instead of:

out[prop] = propval

I can send a pull request with the above fix, if you wish. But since it is a one-liner, hopefully you can add the change yourself. If I am misunderstanding the as_dict() method and my expected values are not correct, could you please provide some additional information about the method and what is expected?

Failing on arrays of mixed tuples

I'm new to both JSON Schema and this module, so I'm not sure where my problem lies, but the ObjectBuilder is throwing an AttributeError:

  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/python_jsonschema_objects-0.2.1+4.g54f3fc8-py3.6.egg/python_jsonschema_objects/classbuilder.py", line 465, in _construct
    elif clsdata.get('type') == 'array' and 'items' in clsdata:
AttributeError: 'list' object has no attribute 'get'

on this schema:

{
  "id": "file:///ipums-citation.json",
  "title": "IPUMSCitation",
  "type": "object",
  "properties": {
    "identifiers": {
      "description": "Unique identifiers for this citation",
      "type": "array",
      "items": [
        { "description": "Type of identifier",
          "type": "string",
          "enum": ["doi","isbn","issn"]
        },
        { "description": "The identifier",
          "type": "string"
        }
      ],
      "uniqueItems": true
    }
  }
}

I don't have this issue when I use arrays where each item has the same schema. Is my schema definition incomplete, or is this a bug or omission within this module?

Thanks!!

Clean up non-header imports

I spent a couple hours trying to fix #17 locally and was bashing my head against the wall until I thought to look for imports outside of the header. It would be greatly appreciated if those are all moved up.

"RefResolutionError" exception raised when resolving "$ref" with no prefix (file:///)

I have 2 simple json schemas, and second schema contains refs to definitions from first. In my case, all refs looks like:

    "person": {
      "description": "Person data and document",
      "$ref": "core.json#/definitions/personDataDocument"
    },

i.e. without specify prefix (python-jsonschema-objects supports relative file refs only in format like "$ref": "file:///core.json#/definitions/personDataDocument"). This can be seen in __init__.py:83. For me specifying prefix is not always good (eg if schemas was placed into web server).

Validate types and requirements in $ref definitions

Hello there,

thank you for your useful python package! I ran into an issue when using the validation function. My test json passes validation although the value of "price" is of type string while the schema requires it to be of type number.
(FYI Elements listed require within definitions could also be dropped without failing validation, which is not included in this example).

Here ist my example:

import python_jsonschema_objects as pjs

builder = pjs.ObjectBuilder(schema)
builder.validate(test)

My json (test):

{
    "id": 42,
    "cars": [{
        "name": "test",
        "price": "321"
    }]
}

My schema (schema):

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "test",
    "type": "object",
    "description": "testtest",
    "definitions": {
        "cars": {
            "type": "array",
            "properties": {
                "name": {
                    "type": "string"
                },
                "price": {
                    "type": "number"
                }
            },
            "required": ["name", "price"]
        },
        "properties": {
            "id": {
                "type": "integer"
            },
            "cars": {
                "$ref": "#/definitions/cars"
            }
        },
        "required": [
            "id",
            "cars"
        ]
    }
}

Feature request: implement default values

Would it be possible for the auto-generated class to implement any default values as specified in the JSON schema? Right now if I have a property defined as:

    "sample-boolean": {
      "description": "A sample boolean",
      "type": "boolean",
      "default": false
    }

and I instantiate an object of the generated class using obj = MyClass.from_json('{}') (or any JSON that does not include "sample-boolean") then obj.sample-boolean will be None instead of false.

Implementing the defaults would be a useful feature, I think.

ImportError: cannot import name Draft4Validator

Seems like the dependency over json_schema has updated itself ,
Need to use from json_schema import validate instead of Draft4Validator .

Traceback:
Traceback (most recent call last):
File "generate_schema.py", line 8, in
import python_jsonschema_objects as pjs
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/init.py", line 2, in
from jsonschema import Draft4Validator
ImportError: cannot import name Draft4Validator

NotImplementedError: anyOf is not supported as bare property

This code
``
with open('ComputerSystemCollection.json') as schema_file:
root_schema = json.load(schema_file)

builder = pjso.ObjectBuilder (root_schema)
ns = builder.build_classes()
``

to generate binding for the following schema:
http://redfish.dmtf.org/schemas/v1/ComputerSystemCollection.json

results in the following error:

Traceback (most recent call last):
File "./test.py", line 14, in
ns = builder.build_classes()
File "/usr/lib/python2.7/site-packages/python_jsonschema_objects/init.py", line 78, in build_classes
builder.construct(uri, defn)
File "/usr/lib/python2.7/site-packages/python_jsonschema_objects/classbuilder.py", line 418, in construct
ret = self._construct(uri, _args, *_kw)
File "/usr/lib/python2.7/site-packages/python_jsonschema_objects/classbuilder.py", line 426, in _construct
"anyOf is not supported as bare property")
NotImplementedError: anyOf is not supported as bare property

How to use <anonymous> objects?

I'm using a json schema that has several nested layers of objects, and when I load it up, the ObjectBuilder seems to label any of the "child" objects as name<anonymous>. I'm not sure if this is expected behavior or not, and if so, how I'm supposed to reference them when building my JSON document. I'm including a simplified example below and a snippet of code to show what I've been attempting so far.

I haven't seen any mention of this in the documentation so I'm hoping someone can point me in the right direction.

Thanks!

{
    "title": "Print Job",
    "$schema": "http://json-schema.org/draft-04/schema#",
    "additionalProperties": false,
    "description": "Print job properties",
    "properties": {
        "format": {
            "description": "File format of the imposed print job",
            "enum": [
                "pdf"
            ]
        },
        "output": {
            "additionalProperties": false,
            "description": "Print job output properties",
            "properties": {
                "mimeType": {
                  "description": "MIME type of the Printer Description Language file used for this job.",
                  "type": "string"
                },
                "name": {
                  "description": "Name of the job.",
                  "type": "string"
                },
                "userName": {
                  "description": "User name of the printer operator.",
                  "type": "string"
                }
            },
            "required": [
                "userName"
            ],
            "type": "object"
        }
    },
    "required": [
        "format",
        "output"
    ],
    "type": "object"
}
import logging
logging.basicConfig(level=logging.DEBUG)

import python_jsonschema_objects as pjs

schema = 'printJob.json'

builder = pjs.ObjectBuilder(schema)

ns = builder.build_classes()
printJob = ns.PrintJob

pj = printJob()

# Here's where I think I'm going wrong.  This doesn't feel right...
output = getattr(ns, "Output<anonymous>")
o = output()
o.mimeType = "some value"
o.userName = "bob"
pj.output = o

Log output:

DEBUG:python_jsonschema_objects.classbuilder:Constructing print_job
DEBUG:python_jsonschema_objects.classbuilder:Building object print_job
DEBUG:python_jsonschema_objects.classbuilder:Constructing print_job/output_<anonymous>
DEBUG:python_jsonschema_objects.classbuilder:Building object print_job/output_<anonymous>
DEBUG:python_jsonschema_objects.classbuilder:Constructing print_job/output_<anonymous>/mimeType
DEBUG:python_jsonschema_objects.classbuilder:Constructed <class 'python_jsonschema_objects.classbuilder.print_job/output_<anonymous>/mimeType'>
DEBUG:python_jsonschema_objects.classbuilder:Constructing print_job/output_<anonymous>/userName
DEBUG:python_jsonschema_objects.classbuilder:Constructed <class 'python_jsonschema_objects.classbuilder.print_job/output_<anonymous>/userName'>
DEBUG:python_jsonschema_objects.classbuilder:Constructing print_job/output_<anonymous>/name
DEBUG:python_jsonschema_objects.classbuilder:Constructed <class 'python_jsonschema_objects.classbuilder.print_job/output_<anonymous>/name'>
DEBUG:python_jsonschema_objects.classbuilder:Constructed <class 'abc.output_<anonymous>'>
DEBUG:python_jsonschema_objects.classbuilder:Constructing print_job/format
DEBUG:python_jsonschema_objects.classbuilder:Constructed <class 'python_jsonschema_objects.classbuilder.print_job/format'>
DEBUG:python_jsonschema_objects.classbuilder:Constructed <class 'abc.print_job'>

class-builder fails to handle object defined with multiple types

Hey there, first of all thanks for your work this awesome library.

I think we found an issue in class_build wrt. multi-type values.

This

import python_jsonschema_objects
schema = {
    "$schema": "http://json-schema.org/schema#",
    "id": "test",
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "email": {"type": ["string", "null"]},
    },
    "required": ["email"]
}
 builder = python_jsonschema_objects.ObjectBuilder(schema)
 builder.build_classes()

Results in:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-7dcbdab4421b> in <module>()
----> 1 builder.build_classes()

/usr/local/lib/python2.7/site-packages/python_jsonschema_objects/__init__.pyc in build_classes(self)
     82         nm = inflection.parameterize(unicode(nm), '_')
     83 
---> 84         builder.construct(nm, self.schema)
     85 
     86         return (

/usr/local/lib/python2.7/site-packages/python_jsonschema_objects/classbuilder.pyc in construct(self, uri, *args, **kw)
    285         """ Wrapper to debug things """
    286         logger.debug("Constructing {0}".format(uri))
--> 287         ret = self._construct(uri, *args, **kw)
    288         logger.debug("Constructed {0}".format(ret))
    289         return ret

/usr/local/lib/python2.7/site-packages/python_jsonschema_objects/classbuilder.pyc in _construct(self, uri, clsdata, parent)
    358                 uri,
    359                 clsdata,
--> 360                 parent)
    361             return self.resolved[uri]
    362         elif clsdata.get('type') in ('integer', 'number', 'string', 'boolean'):

/usr/local/lib/python2.7/site-packages/python_jsonschema_objects/classbuilder.pyc in _build_object(self, nm, clsdata, parents)
    494                     'description'] if 'description' in detail else ""
    495                 uri = "{0}/{1}".format(nm, prop)
--> 496                 typ = self.construct(uri, detail)
    497 
    498                 props[prop] = make_property(prop, {'type': typ}, desc)

/usr/local/lib/python2.7/site-packages/python_jsonschema_objects/classbuilder.pyc in construct(self, uri, *args, **kw)
    285         """ Wrapper to debug things """
    286         logger.debug("Constructing {0}".format(uri))
--> 287         ret = self._construct(uri, *args, **kw)
    288         logger.debug("Constructed {0}".format(ret))
    289         return ret

/usr/local/lib/python2.7/site-packages/python_jsonschema_objects/classbuilder.pyc in _construct(self, uri, clsdata, parent)
    370             return obj
    371 
--> 372         elif 'type' in clsdata and issubclass(clsdata['type'], ProtocolBase):
    373             self.resolved[uri] = clsdata.get('type')
    374             return self.resolved[uri]

/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/abc.pyc in __subclasscheck__(cls, subclass)
    182                 return True
    183         # No dice; update negative cache
--> 184         cls._abc_negative_cache.add(subclass)
    185         return False

/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_weakrefset.pyc in add(self, item)
     84         if self._pending_removals:
     85             self._commit_removals()
---> 86         self.data.add(ref(item, self._remove))
     87 
     88     def clear(self):

TypeError: cannot create weak reference to 'list' object

Swagger schema causes exception in build_classes

I'm calling

  builder = pjs.ObjectBuilder('swagger.json')
  ns = builder.build_classes()

on https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v2.0/schema.json. Python 2.7.

I get the following stack (this is just the last bit):

  File "C:\tools\python\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 436, in _build_object
    (ProtocolBase,))
  File "C:\tools\python\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 288, in construct
    ret = self._construct(uri, *args, **kw)
  File "C:\tools\python\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 362, in _construct
    parent)
  File "C:\tools\python\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 498, in _build_object
    typ = self.construct(uri, detail)
  File "C:\tools\python\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 288, in construct
    ret = self._construct(uri, *args, **kw)
  File "C:\tools\python\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 330, in _construct
    clsdata['type'], (ProtocolBase, LiteralValue)):
  File "C:\tools\python\lib\site-packages\python_jsonschema_objects\util.py", line 14, in safe_issubclass
    return issubclass(x, y)
  File "C:\tools\python\lib\abc.py", line 151, in __subclasscheck__
    if subclass in cls._abc_cache:
  File "C:\tools\python\lib\_weakrefset.py", line 75, in __contains__
    return wr in self.data
RuntimeError: maximum recursion depth exceeded in cmp

TypeError exception from build_classes

Hi Chris,
I'm just starting with python-javaschema-objects trying to learn the process. I started with the metaschema from http://json-schema.org/draft-04/schema (figured it was good) and ran the following short program with the debug messaging activated:

builder = pjs.ObjectBuilder(METASCHEMA)
print "builder:", builder
ns = builder.build_classes()
print "ns:", ns

METASCHEMA is the path to the file containing the schema. The output is below.
(This is python 2.7.8 on windows 7.)
Thanks, Craig

-> python test1.py
builder: <python_jsonschema_objects.ObjectBuilder object at 0x01718A90>
DEBUG:root:Constructing http://json-schema.org/draft-04/schema#/definitions/simpleTypes
DEBUG:root:Constructed <class 'python_jsonschema_objects.classbuilder.http://json-schema.org/draft-04/schema#/definitions/simpleTypes'>
DEBUG:root:Constructing http://json-schema.org/draft-04/schema#/definitions/schemaArray
Traceback (most recent call last):
File "test1.py", line 25, in
ns = builder.build_classes()
File "c:\Users\Craig.virtualenvs\stillsim\lib\site-packages\python_jsonschema_objects__init__.py", line 79, in build_classes
builder.construct(uri, defn)
File "c:\Users\Craig.virtualenvs\stillsim\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 287, in construct
ret = self._construct(uri, _args, *_kw)
File "c:\Users\Craig.virtualenvs\stillsim\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 372, in _construct
elif 'type' in clsdata and issubclass(clsdata['type'], ProtocolBase):
File "c:\Users\Craig.virtualenvs\stillsim\lib\abc.py", line 184, in subclasscheck
cls._abc_negative_cache.add(subclass)
File "c:\Users\Craig.virtualenvs\stillsim\lib_weakrefset.py", line 86, in add
self.data.add(ref(item, self._remove))
TypeError: cannot create weak reference to 'unicode' object

Maximum validation is wrong

Hello,

When trying to validate a schema with maximum limitation, we get an exception:

Traceback (most recent call last):
File "get_metadata.py", line 17, in
resp.DiagnosticsIntervalSeconds = 3600
File "C:\Python27\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 97, in setattr
prop.fset(self, val)
File "C:\Python27\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 620, in setprop
validator = info'type'
File "C:\Python27\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 207, in init
self.validate()
File "C:\Python27\lib\site-packages\python_jsonschema_objects\classbuilder.py", line 236, in validate
validator(paramval, self._value, info)
File "C:\Python27\lib\site-packages\python_jsonschema_objects\validators.py", line 54, in maximum
exclusive = info.get('exclusiveMaximum')
NameError: global name 'info' is not defined

The problem is with the maximum validation code. Need to change from:

@registry.register()
def maximum(param, value, type_data):
    exclusive = info.get('exclusiveMaximum')
    if exclusive:
        if value < param:
            raise ValidationError(
                "{0} was more than {1}".format(value, param))
    elif value <= param:
        raise ValidationError(
            "{0} was more than or equal to {1}".format(value, param))

to:

@registry.register()
def maximum(param, value, type_data):
    exclusive = type_data.get('exclusiveMaximum')
    if exclusive:
        if value < param:
            raise ValidationError(
                "{0} was more than {1}".format(value, param))
    elif value >= param:
        raise ValidationError(
            "{0} was more than or equal to {1}".format(value, param))

Literal Types no longer can be directly operated on

With the new support for default values for literals (which is great), doing stuff like:
object.value + 1 or object.name.startswith('John') no longer works.

from python_jsonschema_objects.classbuilder import MakeLiteral
# expect 10, get an error that 'test' + 'test' is not defined
MakeLiteral('test', 'integer', 5) + MakeLiteral('test', 'integer', 5) 
# expect "hello world", get an error that 'test' + 'test' is not defined
MakeLiteral('test', 'string', 'hello ') + MakeLiteral('test', 'integer', ' world!') 

There's a few quick fix: use str(), float(), int() to get the _value or just do ._value everywhere.

However, it'd be great if we just dispatch the calls to the underlying literals.

i have a gist that is a quick-and-dirty implementation. i think that an improved implementation might actually run MakeLiteral and return a value that is wrapped (and validated).

minItems and maxItems not enforced for array length

ISSUE SUMMARY

The minItems and maxItems parameters appear to have no effect on array size.

In the following example, I would expect test2 and test3 to succeed, and test1 and test4 to fail. All succeed in pjs version 0.2.1.

EXAMPLE CODE

import json
import python_jsonschema_objects as pjs

schema_text = """
{
"title": "mintest",
"type": "object",
"properties": {
"rules": {
"type": "array",
"items": {"type": "string"},
"minItems": 2,
"maxItems": 3
}
},
"required": ["rules"]
}
"""

schema_dict = json.loads(schema_text)

builder = pjs.ObjectBuilder(schema_dict)
ns = builder.build_classes(strict=True)
mintest = ns.Mintest

test1 = mintest(rules=['fred'])
test2 = mintest(rules=['fred', 'wilma'])
test3 = mintest(rules=['fred', 'wilma', 'pebbles'])
test4 = mintest(rules=['fred', 'wilma', 'pebbles', 'dino'])

OUTPUT

test1 = mintest(rules=['fred'])
test2 = mintest(rules=['fred', 'wilma'])
test3 = mintest(rules=['fred', 'wilma', 'pebbles'])
test4 = mintest(rules=['fred', 'wilma', 'pebbles', 'dino'])

Note no exceptions.

DIAGNOSIS

I added debug lines to wrapper_types.py, class ArrayWrapper, method validate_length:

    print 'validating length', repr(self)
    print 'minItems', getattr(self, 'minItems', None)
    print 'maxItems', getattr(self, 'maxItems', None)

The new output looks like this:

test1 = mintest(rules=['fred'])
validating length <mintest/rules_<anonymous_field>=['fred']>
minItems None
maxItems None
validating length <mintest/rules_<anonymous_field>=<mintest/rules_<anonymous_field>=['fred']>>
minItems None
maxItems None
test2 = mintest(rules=['fred', 'wilma'])
validating length <mintest/rules_<anonymous_field>=['fred', 'wilma']>
minItems None
maxItems None
validating length <mintest/rules_<anonymous_field>=<mintest/rules_<anonymous_field>=['fred', 'wilma']>>
minItems None
maxItems None
test3 = mintest(rules=['fred', 'wilma', 'pebbles'])
validating length <mintest/rules_<anonymous_field>=['fred', 'wilma', 'pebbles']>
minItems None
maxItems None
validating length <mintest/rules_<anonymous_field>=<mintest/rules_<anonymous_field>=['fred', 'wilma', 'pebbles']>>
minItems None
maxItems None
test4 = mintest(rules=['fred', 'wilma', 'pebbles', 'dino'])
validating length <mintest/rules_<anonymous_field>=['fred', 'wilma', 'pebbles', 'dino']>
minItems None
maxItems None
validating length <mintest/rules_<anonymous_field>=<mintest/rules_<anonymous_field>=['fred', 'wilma', 'pebbles', 'dino']>>
minItems None
maxItems None

Apparently the minTest attribute is not being associated with the array.

The same is true for maxItems

REQUEST

Am I doing this right?

How would you recommend applying length rules for arrays?

calling serialize() mutates representation

Calling serialize() changes the representation of a pjs object. Fortunately, this doesn't seem to affect the serialization result or the equality (==) of such objects. I can't tell whether this is a bug or merely an oddity.

In the following example, notice the additional #/definitions/Location and #/definitions/Allele after each serialize() call in Out[24], Out[26], and Out[28].

In [30]: python_jsonschema_objects.__version__
Out[30]: '0.2.1'

In [23]: b = models.Vmcbundle(locations=[l.as_dict()], alleles=[a.as_dict()])

In [24]: b
Out[24]: <vmcbundle meta=None haplotypes=None genotypes=None locations=<#/definitions/Location=[{'position': {'end': 42, 'start': 42}, 'sequence_id': 'VMC:GS_01234', 'id': 'VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_'}]> alleles=<#/definitions/Allele=[{'state': 'A', 'location_id': 'VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_', 'id': 'VMC:A_GeingscEDXEw3aHbDngt5PVS-r1pbqyV'}]>>

In [25]: b.serialize()
Out[25]: '{"locations": [{"position": {"end": 42, "start": 42}, "sequence_id": "VMC:GS_01234", "id": "VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_"}], "alleles": [{"state": "A", "location_id": "VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_", "id": "VMC:A_GeingscEDXEw3aHbDngt5PVS-r1pbqyV"}]}'

In [26]: b
Out[26]: <vmcbundle meta=None haplotypes=None genotypes=None locations=<#/definitions/Location=<#/definitions/Location=[{'position': {'end': 42, 'start': 42}, 'sequence_id': 'VMC:GS_01234', 'id': 'VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_'}]>> alleles=<#/definitions/Allele=<#/definitions/Allele=[{'state': 'A', 'location_id': 'VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_', 'id': 'VMC:A_GeingscEDXEw3aHbDngt5PVS-r1pbqyV'}]>>>

In [27]: b.serialize()
Out[27]: '{"locations": [{"position": {"end": 42, "start": 42}, "sequence_id": "VMC:GS_01234", "id": "VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_"}], "alleles": [{"state": "A", "location_id": "VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_", "id": "VMC:A_GeingscEDXEw3aHbDngt5PVS-r1pbqyV"}]}'

In [28]: b
Out[28]: <vmcbundle meta=None haplotypes=None genotypes=None locations=<#/definitions/Location=<#/definitions/Location=<#/definitions/Location=[{'position': {'end': 42, 'start': 42}, 'sequence_id': 'VMC:GS_01234', 'id': 'VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_'}]>>> alleles=<#/definitions/Allele=<#/definitions/Allele=<#/definitions/Allele=[{'state': 'A', 'location_id': 'VMC:L_72pn8Yi-9WyoreirKOH7bc-Vx_Jjkdp_', 'id': 'VMC:A_GeingscEDXEw3aHbDngt5PVS-r1pbqyV'}]>>>>

Schema is here: https://raw.githubusercontent.com/ga4gh/vmc/dev/schema/vmcbundle.json

Bad handling of arrays of mixed types objects

(or at least inconsistent with jsonschema)

Here is a minimal test case:

schema_good = {
    'title': 'example',
    'type': 'array',
    'items': {
        'oneOf': [
            {
                'type': 'object',
                'properties': {
                    'a': {
                        'type': 'string',
                    }
                },
                'required': [ 'a' ],
            },
            {
                'type': 'object',
                'properties': {
                    'b': {
                        'type': 'string',
                    }
                },
                'required': [ 'b' ],
            },
        ]
    },
}

instance = [{'a': ''}, {'b': ''}]

jsonschema validates our instance but build_classes() crashes:

Traceback (most recent call last):
  File "schema.py", line 70, in <module>
    pjs.ObjectBuilder(schema_bad).build_classes()
  File ".../python_jsonschema_objects/__init__.py", line 83, in build_classes
    builder.construct(nm, self.schema)
  File ".../python_jsonschema_objects/classbuilder.py", line 418, in construct
    ret = self._construct(uri, *args, **kw)
  File ".../python_jsonschema_objects/classbuilder.py", line 474, in _construct
    **clsdata_copy)
  File ".../python-jsonschema-objects/python_jsonschema_objects/validators.py", line 317, in create
    elif isdict and item_constraint['type'] == 'array':
KeyError: 'type'

If we remove the required arrays in both objects, build_classes() terminates but jsonschema is unable to validate the instance anymore:

jsonschema.exceptions.ValidationError: {'a': ''} is valid under each of {'properties': {'b': {'type': 'string'}}, 'type': 'object'}, {'properties': {'a': {'type': 'string'}}, 'type': 'object'}

Failed validating 'oneOf' in schema['items']:
    {'oneOf': [{'properties': {'a': {'type': 'string'}}, 'type': 'object'},
               {'properties': {'b': {'type': 'string'}}, 'type': 'object'}]}

On instance[0]:
    {'a': ''}

following $refs with relative file paths

Hey, would someone be able to tell me how to correctly format relative file paths or my calls to ObjectBuilder() so that build_classes() works?

What I am trying:

builder = pjs.ObjectBuilder('facts.json')
ns = builder.build_classes()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Python/2.7/site-packages/python_jsonschema_objects/__init__.py", line 83, in build_classes
    builder.construct(nm, self.schema)
  File "/Library/Python/2.7/site-packages/python_jsonschema_objects/classbuilder.py", line 288, in construct
    ret = self._construct(uri, *args, **kw)
  File "/Library/Python/2.7/site-packages/python_jsonschema_objects/classbuilder.py", line 362, in _construct
    parent)
  File "/Library/Python/2.7/site-packages/python_jsonschema_objects/classbuilder.py", line 458, in _build_object
    typ = self.construct(uri, detail['items'])
  File "/Library/Python/2.7/site-packages/python_jsonschema_objects/classbuilder.py", line 288, in construct
    ret = self._construct(uri, *args, **kw)
  File "/Library/Python/2.7/site-packages/python_jsonschema_objects/classbuilder.py", line 341, in _construct
    with self.resolver.resolving(uri) as resolved:
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 17, in __enter__
    return self.gen.next()
  File "/Library/Python/2.7/site-packages/jsonschema/validators.py", line 296, in resolving
    raise RefResolutionError(exc)
jsonschema.exceptions.RefResolutionError: [Errno 2] No such file or directory: u'ase.json'

facts.json:

{
    "title": "facts schema",
    "type": "object",
    "properties": {
        "interfaces": {
            "type": "array",
            "items": {"$ref": "base.json#/definitions/interface"}
        },
        "os": {"type": "string"},
        "platform": {"type": "string"},
        "hostname": {"type": "string"},
        "kickstart_image": {"type": "string"},
        "last_reboot_reason": {"type": "string"},
        "modules": {
            "type": "array",
            "items": {"$ref": "file://base.json#/definitions/module"}
        },
        "power_supply_info": {
            "type": "array",
            "items": {"$ref": "file://base.json#/definitions/ps_info"}
        },
        "fan_info": {
            "type": "array",
            "items": {"$ref": "file://base.json#/definitions/fans"}
        },
        "vlan_list": {
            "type": "array"
        }
    },
    "required": ["platform"]
}

base.json (same directory as facts.json):

{
    "title": "base schema",
    "definitions": {
        "interface": {
            "type": "object",
            "properties": {
                "interface": {"type": "string"},
                "description": {"type": ["string", "null"]},
                "state": {"type": "string"},
                "vlan": {"type": ["string", "null", "number"]},
                "duplex": {"type": "string"},
                "speed": {"type": ["string", "number"]},
                "type": {"type": "string"}
            },
            "required": ["interface", "description", "state", "duplex", "speed"]
        },

        "module": {
            "type": "object",
            "properties": {
                "ports": {"type": "number"},
                "type": {"type": "string"},
                "model": {"type": "string"},
                "status": {"type": "string"}
            }
        },

        "ps_info": {
            "type": "object",
            "properties": {
                "number": {"type": "number"},
                "model": {"type": "string"},
                "actual_output": {"type": "string"},
                "actual_input": {"type": ["string", "null"]},
                "total_capacity": {"type": "string"},
                "status": {"type": "string"}
            },
            "required": ["number", "model", "status"]
        },

        "fan_info": {
            "id": "#fans",
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "model": {"type": "string"},
                "hw_ver": {"type": "string"},
                "direction": {"type": "string"},
                "status": {"type": "string"}
            },
            "required": ["name", "status"]
        }
    }
}

Invalid $ref resolving upon immediate $ref

This json schema fails:
{ "$ref": "memory:OtherClass", "title": "test" }

when execute this code:
`
builder = pjs.ObjectBuilder(scheme_for_pjs, resolved={
"otherClass": {"type":"object", "properties":{"name":{"type":"string"}}}
})

MyClass = getattr(builder.build_classes(), "Test")
`

I get:

jsonschema.exceptions.RefResolutionError: unknown url type: 'test'

Add strict mode to object creation (required)

Hi,
the doc says:
""" An instance of a class generated from the provided
schema. All properties will be validated according to
the definitions provided. However, whether or not all required
properties have been provide will not be validated.
""

it's very important for me that all required properties are in the object, and i would like to throw an error in case missing required field on creation time, also i think it's a logical behavior .

i would like to add "strict" key-argument to build_classes,
in case of strict mode the object will validate itself upon creation,

already made a local fork , can i make a pr and add this?

Json reader pipeline

This project seems very focused on exporting json documents conforming to a specified schema specification.

I want to go the other way, I have schema specification and a bunch of json documents that I need to parse. I was hoping to find an example showing this, how to load and validate an existing json document into a class instance generated by this framework. I was expecting something like

try:
    person = ns.load(jsonData)
    print('firstName: ' + person.firstName + ', lastName: ' + person.lastName)
except ValidationError as e:
    print('syntax error: ' + str(e))

where the jsonData would be validated against the loaded schema

With this workflow it would also be nice if the generated bindings could be stored as python code instead of being generated during runtime. If I have a schema that does not change much I can reuse the generated classes if I'm able to import them in my parser. Updating the generated classes can easily be managed by any build system that detects changes updated modification dates on the scheme files.

Class names in namespace

It is quite wired that every title and id converted.
For example:
FooBar -> Foobar
Foo Bar -> FooBar
foo_bar -> FooBar

My proposal would be that that conversion shall be done only for title. So if user give an 'id' for the schema object it shall be used as class name without any modification. In case of this 'id' is not a valid python identifier then builder shall throw an appropriate exception instead of converting it silently.

Support for the "format" field from JSON spec

Any thoughts to if you are going to support validation type "format" and type coercion based on that?
Would you be open to a pull request that add such a thing, if you are not interested?

Feature request

Following features or helper methods wud be nice, this library will truly become a service to handle all sorts of flat file

A. Generate n objects with default values corresponding to built class
B. De/serialize all fields
C. De/serialze selected set of fields
D. Sorting of objects generated based on builder class w.r.t certain fields

Consider "None" as a valid string type

Many external systems/libraries (sqlite) interchange None and empty strings. When directly assigning a jsonschema object string attribute, the check_string_type will throw a validation error if the value is None. Working around this requires a conversion of all None's to empty strings or verbose None checks before assignment.

Would it make sense to safely modify check_string_type to ignore any 'None' values instead of throwing an error?

Properties with multiple types are not parsed correctly

http://json-schema.org/latest/json-schema-validation.html#anchor79

Example property

    "claimed_by": {
        "id": "claimed",
        "type": ["string", "null"],
        "description": "Robots Only. The human agent that has claimed this robot.",
        "required": false
    },

Traceback (most recent call last):
File "/home/idanforth/fetch/src/sandbox/fetchcore/test/unit/test_scheduler.py", line 58, in setUp
agent_ns = agent_builder.build_classes()
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/init.py", line 83, in build_classes
builder.construct(nm, self.schema)
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/classbuilder.py", line 288, in construct
ret = self._construct(uri, _args, *_kw)
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/classbuilder.py", line 362, in _construct
parent)
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/classbuilder.py", line 498, in _build_object
typ = self.construct(uri, detail)
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/classbuilder.py", line 288, in construct
ret = self._construct(uri, _args, *_kw)
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/classbuilder.py", line 380, in _construct
"no type and no reference".format(clsdata))
NotImplementedError: Unable to parse schema object '{'raw_name': u'claimed_by', u'required': False, u'type': [u'string'
, u'null'], u'id': u'claimed', u'description': u'Robots Only. The human agent that has claimed this robot.'}' with no t
ype and no reference

RPM build failed: python3 setup.py bdist_rpm

ACTIONS

wget 'https://pypi.python.org/packages/source/p/python_jsonschema_objects/python_jsonschema_objects-0.0.15.tar.gz#md5=13a9ee120bf0ba0356ec562ee9ae94ec'
tar -xzf python_jsonschema_objects-0.0.15.tar.gz
cd python_jsonschema_objects-0.0.15/
python3 setup.py bdist_rpm

RESULT

requirements.txt
running bdist_rpm
running egg_info
...
byte-compiling /tmp/python_jsonschema_objects-0.0.15/build/bdist.linux-x86_64/rpm/BUILDROOT/python_jsonschema_objects-0.0.15-1.x86_64/usr/lib/python3.4/site-packages/test/test_util.py to test_util.cpython-34.pyc
File "/usr/lib/python3.4/site-packages/test/test_util.py", line 0
SyntaxError: unknown encoding: spec

writing byte-compilation script '/tmp/tmppszppvxh.py'
/usr/bin/python3 -O /tmp/tmppszppvxh.py
File "/usr/lib/python3.4/site-packages/test/test.py", line 0
SyntaxError: unknown encoding: spec

File "/usr/lib/python3.4/site-packages/test/test_util.py", line 0
SyntaxError: unknown encoding: spec
...
error: command 'rpmbuild' failed with exit status 1

Arrays without "items" fail to serialize

I don't believe "items" is required by the spec, but appears to be required by the serializer

Traceback (most recent call last):
File "/home/idanforth/fetch/src/sandbox/fetchcore/test/unit/test_scheduler.py", line 99, in test_init_scheduler
print new_task.serialize()
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/classbuilder.py", line 154, in serialize
return enc.encode(self)
File "/usr/lib/python2.7/json/encoder.py", line 207, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 270, in iterencode
return _iterencode(o, 0)
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/util.py", line 40, in default
props[raw] = getattr(obj, trans)
File "/usr/local/lib/python2.7/dist-packages/python_jsonschema_objects/classbuilder.py", line 141, in getattr
name, self.class.name))
AttributeError: errors is not a valid property of task

Object property that fails:

    "errors": {
        "id": "errors",
        "type": "array",
        "description": "Errors not associated with any goals",
        "required": false
    },

Object property that succeeds:

    "errors": {
        "id": "errors",
        "type": "array",
        "items": {
            "type": "string"
        },
        "description": "Errors not associated with any goals",
        "required": false
    },

Hard coding specific version of dependencies is problematic

requirements.txt currently hardcodes jsonschema 2.3.0. That means I have to use it when using automated packaging tools like pip (or in my case pex).

jsonschema is up to version 2.5.1 though, so I can't use newer jsonschema at the same time as python-jsonschema-objects.

As an alternative I suggest requirements.txt include a minimal version, e.g. jsonschema>=2.3.0.

Array validation works, but doesn't convert subclasses

While array types are validated according to the value of any 'items' property given to them, the arrays are actually returned raw when instantiated.

This means that the input validation will no longer be applied to sub-array objects.

Fix is probably to create a validating subclass of list. For that matter, ProtocolBase should probably subclass 'dict' or use a dictmixin

Array items cannot use 'oneOf' in 'items' specification.

Array validators use a different handling path than the standard object
construction code, and that path doesn't have support for items definitions
that aren't actual descriptors (i.e. that use oneOf, allOf or friends).

The workaround for this that works is to create an intermediate object definition
that has a base oneOf and then reference items to it:

{
    "$schema": "http://json-schema.org/schema#",
    "id": "test",
    "type": "object",
    "properties": {
        "list": {
            "type": "array",
            "items": {"$ref": "#/definitions/foo"},
            }
        },
    "definitions": {
        "foo": {
            "type": {"oneOf": [
                {"type": "string"}
                ]}
            }
        }
    }

Doing this allows the main classbuilder resolver to properly
resolve the oneOf before passing it to items

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.