diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 0af63c5..f884662 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,11 +2,12 @@
CHANGELOG
*********
-`v1.0.16`_ (2019-10-XX)
+`v1.0.16`_ (2019-11-15)
=======================
-* Improved XMLResource with zip files interface and lazy
+* Improved XMLResource class for working with compressed files
* Fix for validation with XSD wildcards and 'lax' process content
-* Fix for issue #1...
+* Fix ambiguous items validation for xs:choice and xs:sequence models
+* Dozens of W3C's failed tests fixed
`v1.0.15`_ (2019-10-13)
=======================
@@ -271,3 +272,4 @@ v0.9.6 (2017-05-05)
.. _v1.0.13: https://github.com/brunato/xmlschema/compare/v1.0.11...v1.0.13
.. _v1.0.14: https://github.com/brunato/xmlschema/compare/v1.0.13...v1.0.14
.. _v1.0.15: https://github.com/brunato/xmlschema/compare/v1.0.14...v1.0.15
+.. _v1.0.16: https://github.com/brunato/xmlschema/compare/v1.0.15...v1.0.16
diff --git a/xmlschema/documents.py b/xmlschema/documents.py
index 439a8c9..02e6471 100644
--- a/xmlschema/documents.py
+++ b/xmlschema/documents.py
@@ -171,7 +171,7 @@ def from_json(source, schema, path=None, converter=None, json_options=None, **kw
:param source: can be a string or a :meth:`read()` supporting file-like object \
containing the JSON document.
- :param schema: an :class:`XMLSchema` instance.
+ :param schema: an :class:`XMLSchema` or an :class:`XMLSchema11` instance.
:param path: is an optional XPath expression for selecting the element of the schema \
that matches the data that has to be encoded. For default the first global element of \
the schema is used.
diff --git a/xmlschema/tests/test_models.py b/xmlschema/tests/test_models.py
index b671cd6..a02b9b7 100644
--- a/xmlschema/tests/test_models.py
+++ b/xmlschema/tests/test_models.py
@@ -551,14 +551,14 @@ class TestModelValidation(XsdValidatorTestCase):
-
+
""")
- self.assertIsNone(schema.validate(''))
+ self.assertIsNone(schema.validate(''))
schema = self.schema_class(
"""
@@ -577,6 +577,8 @@ class TestModelValidation(XsdValidatorTestCase):
self.assertIsNone(schema.validate(''))
self.assertIsNone(schema.validate(''))
+ def test_sequence_model_with_nested_choice_model(self):
+
schema = self.schema_class(
"""
@@ -589,7 +591,7 @@ class TestModelValidation(XsdValidatorTestCase):
-
+
@@ -597,9 +599,26 @@ class TestModelValidation(XsdValidatorTestCase):
""")
- self.assertIsNone(schema.validate(''))
- # self.assertIsNone(schema.validate(''))
- # self.assertIsNone(schema.validate(''))
+ self.assertIsNone(schema.validate(''))
+ self.assertIsNone(schema.validate(''))
+ self.assertIsNone(schema.validate(''))
+
+ def test_sequence_model_with_optional_elements(self):
+ schema = self.schema_class(
+ """
+
+
+
+
+
+
+
+
+
+
+ """)
+
+ self.assertIsNone(schema.validate(''))
def test_choice_model_with_extended_occurs(self):
schema = self.schema_class(
diff --git a/xmlschema/validators/models.py b/xmlschema/validators/models.py
index c26859d..96cfaaf 100644
--- a/xmlschema/validators/models.py
+++ b/xmlschema/validators/models.py
@@ -453,12 +453,12 @@ class ModelVisitor(MutableSequence):
if isinstance(item, ModelGroup):
self.group, self.items, self.match = self.pop()
- item_occurs = occurs[item]
if self.group.model == 'choice':
+ item_occurs = occurs[item]
if not item_occurs:
return False
-
item_max_occurs = occurs[(item,)] or item_occurs
+
min_group_occurs = max(1, item_occurs // (item.max_occurs or item_occurs))
max_group_occurs = max(1, item_max_occurs // (item.min_occurs or 1))
@@ -468,31 +468,40 @@ class ModelVisitor(MutableSequence):
self.items = self.iter_group()
self.match = False
- return item.is_missing(max(item_occurs, occurs[(item,)]))
+ return item.is_missing(item_max_occurs)
elif self.group.model == 'all':
return False
- elif item_occurs:
- self.match = True
elif self.match:
pass
+ elif occurs[item]:
+ self.match = True
elif item.is_emptiable():
return False
- elif self.group.min_occurs <= occurs[self.group] or self:
+ elif self.group.min_occurs <= max(occurs[self.group], occurs[(self.group,)]) or self:
return stop_item(self.group)
else:
return True
if item is self.group[-1]:
- if any(occurs[x] for x in self if x is not item):
- self.occurs[self.group] += 1
- else:
- group_occurs = max(1, item_occurs // (item.min_occurs or 1))
- if self.group.is_over(group_occurs):
- group_occurs = self.group.max_occurs
- self.occurs[self.group] += max(1, group_occurs)
+ for k, item2 in enumerate(self.group, start=1):
+ item_occurs = occurs[item2]
+ if not item_occurs:
+ continue
- return item.is_missing(max(item_occurs, occurs[(item,)]))
+ item_max_occurs = occurs[(item2,)] or item_occurs
+ if item_max_occurs == 1 or any(not x.is_emptiable() for x in self.group[k:]):
+ self.occurs[self.group] += 1
+ break
+
+ min_group_occurs = max(1, item_occurs // (item2.max_occurs or item_occurs))
+ max_group_occurs = max(1, item_max_occurs // (item2.min_occurs or 1))
+
+ occurs[self.group] += min_group_occurs
+ occurs[(self.group,)] += max_group_occurs
+ break
+
+ return item.is_missing(max(occurs[item], occurs[(item,)]))
element, occurs = self.element, self.occurs
if element is None:
@@ -514,7 +523,7 @@ class ModelVisitor(MutableSequence):
yield element, occurs[element], [element]
while True:
- while self.group.is_over(occurs[self.group]):
+ while self.group.is_over(max(occurs[self.group], occurs[(self.group,)])):
stop_item(self.group)
obj = next(self.items, None)