Repository: tefra/xsdata
Pull request #945 (2024-02-18 11:14:49)

Avoid flattening elements
## 📒 Description


> All xs:schema elements are classes everything else is either noise or class properties

That naive assumption is what started this project almost 5 years ago. Since then I am trying to get to that milestone by only generating root level elements and complex types, but in order to get there I had to solve the xml schemas puzzle, sometimes making compromises in order for certain things to work. That's why in some occasions I had to be aggressive with the flattening process and to introduce the filtering strategies.

Every year I tried to enable this feature and every year I had so many issues. Something was missing... I can't put my finger on what exactly but it seems this year we will make it. The w3c tests pass, the samples repo build passes.


Resolves #xxxx

## 🔗 What I've Done

- Finally adhere to my naive assumption and only generate the root elements and complex types!

## 💬 Comments

> A place to write any comments to the reviewer.

## 🛫 Checklist

- [ ] Updated docs
- [ ] Added unit-tests
- [ ] [Sample tests](https://github.com/tefra/xsdata-samples) pass
- [ ] [W3C tests](https://github.com/tefra/xsdata-w3c-tests) pass



Patch:
diff --git a/docs/codegen/config.md b/docs/codegen/config.md
index b913ed76..4e9490f0 100644
--- a/docs/codegen/config.md
+++ b/docs/codegen/config.md
@@ -140,21 +140,6 @@ The style of docstrings to create.
 **CLI Option:**
 `-ds, --docstring-style [reStructuredText|NumPy|Google|Accessible|Blank]`
 
-### FilterStrategy
-
-When the class analyzer runs, a lot of types, base classes are flattened, leaving a lot
-of orphan classes. The filter strategy can be used to remove unused classes.
-
-| Style             | Description                                                                 |
-| ----------------- | --------------------------------------------------------------------------- |
-| `all`             | Generate all simple and complex types.                                      |
-| `allGlobals`      | Generate global elements and complex types and all referenced simple types. |
-| `referredGlobals` | Generate global elements and all referenced simple and complex types.       |
-
-**Default Value:** `allGlobals`
-
-**CLI Option:** `-fs, --filter-strategy [all|allGlobals|referredGlobals]`
-
 ### RelativeImports
 
 Generate relative instead of absolute imports.
diff --git a/xsdata/codegen/handlers/filter_classes.py b/xsdata/codegen/handlers/filter_classes.py
index 4219cd1e..7dcc7a59 100644
--- a/xsdata/codegen/handlers/filter_classes.py
+++ b/xsdata/codegen/handlers/filter_classes.py
@@ -3,7 +3,6 @@ from typing import List
 from xsdata.codegen.mixins import ContainerHandlerInterface
 from xsdata.codegen.models import Class
 from xsdata.logger import logger
-from xsdata.models.config import ClassFilterStrategy
 
 
 class FilterClasses(ContainerHandlerInterface):
@@ -15,29 +14,16 @@ class FilterClasses(ContainerHandlerInterface):
         """Main entrypoint to filter the class container.
 
         In order for a class to be considered global it has
-        to be a non-abstract element, a complex type without
-        simple content or a wsdl binding element.
+        to be derived from an element, complexType, a binding
+        operation or message
 
-        Strategies:
-            - Filter all global classes and the referenced simple types.
-            - Filter global classes with references to other global classes.
-            - Filter all classes
+        If no global types exist, all classes will be generated.
         """
-        classes = []
-        filter_strategy = self.container.config.output.filter_strategy
-        if filter_strategy == ClassFilterStrategy.ALL_GLOBALS:
-            classes = self.filter_all_globals()
-        elif filter_strategy == ClassFilterStrategy.REFERRED_GLOBALS:
-            classes = self.filter_referred_globals()
-
+        classes = self.filter_all_globals()
         if classes:
             self.container.set(classes)
-        elif filter_strategy != ClassFilterStrategy.ALL:
-            logger.warning(
-                "The filter strategy '%s' returned no classes,"
-                " will generate all types.",
-                filter_strategy.value,
-            )
+        else:
+            logger.warning("No global types exist, will generate all types.")
 
     def filter_all_globals(self) -> List[Class]:
         """Filter all globals and any referenced types.
@@ -50,27 +36,8 @@ class FilterClasses(ContainerHandlerInterface):
         """
         occurs = set()
         for obj in self.container:
-            if obj.is_global_type:
+            if obj.is_complex_type:
                 occurs.add(obj.ref)
                 occurs.update(obj.references)
 
         return [obj for obj in self.container if obj.ref in occurs]
-
-    def filter_referred_globals(self) -> List[Class]:
-        """Filter globals with any references.
-
-        This filter is trying to remove unused global
-        types.
-
-        Returns:
-            The list of classes for generation.
-        """
-        occurs = set()
-        for obj in self.container:
-            if obj.is_global_type:
-                references = list(obj.references)
-                occurs.update(references)
-                if references:
-                    occurs.add(obj.ref)
-
-        return [obj for obj in self.container if obj.ref in occurs]
diff --git a/xsdata/codegen/handlers/flatten_class_extensions.py b/xsdata/codegen/handlers/flatten_class_extensions.py
index 329f79a1..325c4920 100644
--- a/xsdata/codegen/handlers/flatten_class_extensions.py
+++ b/xsdata/codegen/handlers/flatten_class_extensions.py
@@ -73,7 +73,7 @@ class FlattenClassExtensions(RelativeHandlerInterface):
             target.extensions.remove(extension)
         elif target.is_enumeration:
             self.process_enum_extension(source, target, extension)
-        elif not source.is_complex or source.is_enumeration:
+        elif not source.is_complex_type or source.is_enumeration:
             self.process_simple_extension(source, target, extension)
         else:
             self.process_complex_extension(source, target, extension)
@@ -100,7 +100,7 @@ class FlattenClassExtensions(RelativeHandlerInterface):
         """
         if source.is_enumeration:
             self.merge_enumerations(source, target)
-        elif source.is_simple_type:
+        elif not source.is_complex_type:
             self.merge_enumeration_types(source, target)
         elif len(target.attrs) == 1:
             self.set_default_value(source, target)
@@ -278,7 +278,7 @@ class FlattenClassExtensions(RelativeHandlerInterface):
             target: The target class instance
         """
         if not source.extensions and (
-            source.is_simple_type
+            not source.is_complex_type
             or target.has_suffix_attr
             or (source.has_suffix_attr and target.attrs)
         ):
diff --git a/xsdata/codegen/handlers/process_attributes_types.py b/xsdata/codegen/handlers/process_attributes_types.py
index 2b145b6b..fa157e79 100644
--- a/xsdata/codegen/handlers/process_attributes_types.py
+++ b/xsdata/codegen/handlers/process_attributes_types.py
@@ -132,7 +132,7 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
         conditions = (
             lambda obj: obj.tag == tag,
             lambda obj: tag == Tag.ELEMENT and obj.tag == Tag.COMPLEX_TYPE,
-            lambda obj: not obj.is_complex,
+            lambda obj: not obj.is_complex_type,
             lambda x: True,
         )
 
@@ -157,7 +157,13 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
             return
 
         inner = self.container.find_inner(target, attr_type.qname)
-        if inner.is_simple_type:
+
+        # If the inner class behaves as a simple type flatten it.
+        if (
+            len(inner.attrs) == 1
+            and not inner.attrs[0].xml_type
+            and not inner.extensions
+        ):
             self.copy_attribute_properties(inner, target, attr, attr_type)
             target.inner.remove(inner)
 
@@ -166,8 +172,8 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
 
         Strategies:
             1. Reset absent types with a warning
-            2. Copy attribute properties from a simple type
             3. Copy format restriction from an enumeration
+            2. Copy attribute properties from a simple type
             4. Set circular flag for the rest
 
         Args:
@@ -178,10 +184,7 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
         source = self.find_dependency(attr_type, attr.tag)
         if not source:
             logger.warning("Reset absent type: %s", attr_type.name)
-            use_str = not source or not source.is_complex
-            self.reset_attribute_type(attr_type, use_str)
-        elif source.is_simple_type:
-            self.copy_attribute_properties(source, target, attr, attr_type)
+            self.reset_attribute_type(attr_type, True)
         elif source.is_enumeration:
             attr.restrictions.min_length = None
             attr.restrictions.max_length = None
@@ -189,6 +192,8 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
                 x.restrictions.format for x in source.attrs if x.restrictions.format
             )
             attr_type.reference = id(source)
+        elif not source.is_complex_type:
+            self.copy_attribute_properties(source, target, attr, attr_type)
         elif source.is_element and source.abstract:
             # Substitution groups with abstract elements are used like
             # placeholders and shouldn't be added as standalone fields.
diff --git a/xsdata/codegen/models.py b/xsdata/codegen/models.py
index 65364b41..0c01ae8b 100644
--- a/xsdata/codegen/models.py
+++ b/xsdata/codegen/models.py
@@ -19,8 +19,13 @@ xml_type_map = {
     Tag.ELEMENT: XmlType.ELEMENT,
 }
 
-SIMPLE_TYPES = (Tag.EXTENSION, Tag.LIST, Tag.SIMPLE_TYPE, Tag.UNION)
-GLOBAL_TYPES = (Tag.ELEMENT, Tag.BINDING_OPERATION, Tag.BINDING_MESSAGE, Tag.MESSAGE)
+GLOBAL_TYPES = (
+    Tag.ELEMENT,
+    Tag.COMPLEX_TYPE,
+    Tag.BINDING_OPERATION,
+    Tag.BINDING_MESSAGE,
+    Tag.MESSAGE,
+)
 
 
 @dataclass
@@ -538,11 +543,6 @@ class Class:
         """Return whether at least one of attrs has help content."""
         return any(attr.help and attr.help.strip() for attr in self.attrs)
 
-    @property
-    def is_complex(self) -> bool:
-        """Return whether class represents a xs:element/complex type."""
-        return self.tag in (Tag.ELEMENT, Tag.COMPLEX_TYPE)
-
     @property
     def is_element(self) -> bool:
         """Return whether this class represents a xml element."""
@@ -554,14 +554,12 @@ class Class:
         return len(self.attrs) > 0 and all(attr.is_enumeration for attr in self.attrs)
 
     @property
-    def is_global_type(self) -> bool:
+    def is_complex_type(self) -> bool:
         """Return whether this class represents a root/global class.
 
         Global classes are the only classes that get generated by default.
         """
-        return (not self.abstract and self.tag in GLOBAL_TYPES) or (
-            self.tag == Tag.COMPLEX_TYPE and not self.is_simple_type
-        )
+        return self.tag in GLOBAL_TYPES
 
     @property
     def is_group(self) -> bool:
@@ -590,21 +588,6 @@ class Class:
         """Return whether this instance is derived from a wsdl:operation."""
         return self.tag == Tag.BINDING_OPERATION
 
-    @property
-    def is_simple_type(self) -> bool:
-        """Return whether the class represents a simple type.
-
-        Simple Types:
-            - xs:simpleType/extension/list/union
-            - have only one attr
-            - have no extensions.
-        """
-        return (
-            len(self.attrs) == 1
-            and self.attrs[0].tag in SIMPLE_TYPES
-            and not self.extensions
-        )
-
     @property
     def references(self) -> Iterator[int]:
         """Yield all class object reference numbers."""
diff --git a/xsdata/models/config.py b/xsdata/models/config.py
index 41d0e6e0..85c875c1 100644
--- a/xsdata/models/config.py
+++ b/xsdata/models/config.py
@@ -105,21 +105,6 @@ class DocstringStyle(Enum):
     BLANK = "Blank"
 
 
-class ClassFilterStrategy(Enum):
-    """Class filter strategy enumeration.
-
-    Attributes:
-        ALL: all: Generate all types, discouraged!!!
-        ALL_GLOBALS: allGlobals: Generate all global types
-        REFERRED_GLOBALS: referredGlobals: Generate all global types
-            with at least one reference.
-    """
-
-    ALL = "all"
-    ALL_GLOBALS = "allGlobals"
-    REFERRED_GLOBALS = "referredGlobals"
-
-
 class ObjectType(Enum):
     """Object type enumeration.
 
@@ -230,7 +215,6 @@ class GeneratorOutput:
         format: Output format
         structure_style: Output structure style
         docstring_style: Docstring style
-        filter_strategy: Class filter strategy
         relative_imports: Use relative imports
         compound_fields: Use compound fields for repeatable elements
         max_line_length: Adjust the maximum line length
@@ -249,9 +233,6 @@ class GeneratorOutput:
         default=StructureStyle.FILENAMES, name="Structure"
     )
     docstring_style: DocstringStyle = element(default=DocstringStyle.RST)
-    filter_strategy: ClassFilterStrategy = element(
-        default=ClassFilterStrategy.ALL_GLOBALS
-    )
     relative_imports: bool = element(default=False)
     compound_fields: CompoundFields = element(default_factory=CompoundFields)
     max_line_length: int = attribute(default=79)


Test patch:
diff --git a/tests/codegen/handlers/test_filter_classes.py b/tests/codegen/handlers/test_filter_classes.py
index 237a0806..6e5d87ca 100644
--- a/tests/codegen/handlers/test_filter_classes.py
+++ b/tests/codegen/handlers/test_filter_classes.py
@@ -2,9 +2,9 @@ from unittest import mock
 
 from xsdata.codegen.container import ClassContainer
 from xsdata.codegen.handlers import FilterClasses
-from xsdata.models.config import ClassFilterStrategy, GeneratorConfig
+from xsdata.models.config import GeneratorConfig
 from xsdata.models.enums import Tag
-from xsdata.utils.testing import AttrFactory, ClassFactory, FactoryTestCase
+from xsdata.utils.testing import ClassFactory, FactoryTestCase
 
 
 class FilterClassesTests(FactoryTestCase):
@@ -27,44 +27,18 @@ class FilterClassesTests(FactoryTestCase):
 
         element = ClassFactory.create(tag=Tag.ELEMENT, abstract=True)
 
-        expected = [complex_type, enum_1]
+        expected = [complex_type, enum_1, element]
         self.container.extend([complex_type, enum_1, simple_type, enum_2, element])
         self.handler.run()
         self.assertEqual(expected, list(self.container))
 
-    def test_filter_referred_globals(self):
-        self.container.config.output.filter_strategy = (
-            ClassFilterStrategy.REFERRED_GLOBALS
-        )
-
-        element_1 = ClassFactory.create(tag=Tag.ELEMENT, attrs=AttrFactory.list(2))
-        element_2 = ClassFactory.create(tag=Tag.ELEMENT, attrs=AttrFactory.list(2))
-        enum_1 = ClassFactory.enumeration(2)
-        element_2.attrs[0].types[0].reference = enum_1.ref
-
-        expected = [element_2, enum_1]
-        self.container.extend([element_1, element_2, enum_1])
-        self.handler.run()
-        self.assertEqual(expected, list(self.container))
-
-    def test_filter_all(self):
-        simple_type = ClassFactory.simple_type()
-        enumeration = ClassFactory.enumeration(2)
-        complex_type = ClassFactory.elements(2)
-        self.container.extend([simple_type, enumeration, complex_type])
-        self.container.config.output.filter_strategy = ClassFilterStrategy.ALL
-
-        self.handler.run()
-        self.assertEqual(3, len(list(self.container)))
-
     @mock.patch("xsdata.codegen.handlers.filter_classes.logger.warning")
-    def test_run_with_strategy_not_all_with_no_classes(self, mock_warning):
+    def test_run_with_no_global_types(self, mock_warning):
         classes = [ClassFactory.enumeration(2), ClassFactory.simple_type()]
         self.container.extend(classes)
         self.handler.run()
         self.assertEqual(classes, list(self.container))
 
         mock_warning.assert_called_once_with(
-            "The filter strategy '%s' returned no classes, will generate all types.",
-            "allGlobals",
+            "No global types exist, will generate all types.",
         )
diff --git a/tests/codegen/handlers/test_flatten_class_extensions.py b/tests/codegen/handlers/test_flatten_class_extensions.py
index 0dfb1827..ac215442 100644
--- a/tests/codegen/handlers/test_flatten_class_extensions.py
+++ b/tests/codegen/handlers/test_flatten_class_extensions.py
@@ -385,8 +385,8 @@ class FlattenClassExtensionsTests(FactoryTestCase):
         self.assertTrue(callback(source, target, extension))
 
     def test_should_flatten_extension(self):
-        source = ClassFactory.create()
-        target = ClassFactory.create()
+        source = ClassFactory.create(tag=Tag.COMPLEX_TYPE)
+        target = ClassFactory.create(tag=Tag.ELEMENT)
 
         self.assertFalse(self.processor.should_flatten_extension(source, target))
 
@@ -407,7 +407,7 @@ class FlattenClassExtensionsTests(FactoryTestCase):
         self.assertTrue(self.processor.should_flatten_extension(source, target))
 
         # Source is a simple type
-        source = ClassFactory.create(attrs=[AttrFactory.create(tag=Tag.SIMPLE_TYPE)])
+        source = ClassFactory.simple_type()
         target = ClassFactory.elements(1)
         self.assertTrue(self.processor.should_flatten_extension(source, target))
 
diff --git a/tests/codegen/models/test_class.py b/tests/codegen/models/test_class.py
index 2fb2f43f..537181d3 100644
--- a/tests/codegen/models/test_class.py
+++ b/tests/codegen/models/test_class.py
@@ -1,6 +1,5 @@
 import sys
 
-from xsdata.codegen.models import SIMPLE_TYPES
 from xsdata.exceptions import CodeGenerationError
 from xsdata.models.enums import DataType, Namespace, Tag
 from xsdata.utils.namespaces import build_qname
@@ -102,16 +101,6 @@ class ClassTests(FactoryTestCase):
         obj.attrs[1].index = sys.maxsize
         self.assertTrue(obj.has_suffix_attr)
 
-    def test_property_is_complex(self):
-        obj = ClassFactory.create(tag=Tag.ELEMENT)
-        self.assertTrue(obj.is_complex)
-
-        obj = ClassFactory.create(tag=Tag.COMPLEX_TYPE)
-        self.assertTrue(obj.is_complex)
-
-        obj = ClassFactory.create(tag=Tag.SIMPLE_TYPE)
-        self.assertFalse(obj.is_complex)
-
     def test_property_is_element(self):
         obj = ClassFactory.create(tag=Tag.ELEMENT)
         self.assertTrue(obj.is_element)
@@ -129,21 +118,6 @@ class ClassTests(FactoryTestCase):
         obj.attrs.clear()
         self.assertFalse(obj.is_enumeration)
 
-    def test_property_is_global_type(self):
-        obj = ClassFactory.create(abstract=True, tag=Tag.ELEMENT)
-        self.assertFalse(obj.is_global_type)
-
-        obj = ClassFactory.create(tag=Tag.ELEMENT)
-        self.assertTrue(obj.is_global_type)
-
-        obj = ClassFactory.create(tag=Tag.COMPLEX_TYPE)
-        obj.extensions.append(ExtensionFactory.create())
-        self.assertTrue(obj.is_global_type)
-
-        obj.extensions.clear()
-        obj.attrs.append(AttrFactory.create(tag=Tag.EXTENSION))
-        self.assertFalse(obj.is_global_type)
-
     def test_property_is_restricted(self):
         obj = ClassFactory.create()
         ext = ExtensionFactory.create(tag=Tag.EXTENSION)
@@ -154,21 +128,6 @@ class ClassTests(FactoryTestCase):
         ext.tag = Tag.RESTRICTION
         self.assertTrue(obj.is_restricted)
 
-    def test_property_is_simple_type(self):
-        obj = ClassFactory.elements(2)
-
-        self.assertFalse(obj.is_simple_type)
-
-        obj.attrs.pop()
-        self.assertFalse(obj.is_simple_type)
-
-        for tag in SIMPLE_TYPES:
-            obj.attrs[0].tag = tag
-            self.assertTrue(obj.is_simple_type)
-
-        obj.extensions.append(ExtensionFactory.create())
-        self.assertFalse(obj.is_simple_type)
-
     def test_property_is_group(self):
         self.assertTrue(ClassFactory.create(tag=Tag.GROUP).is_group)
         self.assertTrue(ClassFactory.create(tag=Tag.ATTRIBUTE_GROUP).is_group)
diff --git a/tests/codegen/test_container.py b/tests/codegen/test_container.py
index 71193fac..b86ace5e 100644
--- a/tests/codegen/test_container.py
+++ b/tests/codegen/test_container.py
@@ -145,7 +145,7 @@ class ClassContainerTests(FactoryTestCase):
         container = ClassContainer(config=GeneratorConfig())
         container.extend([complex_type, enum_1, enum_2, simple_type, element])
 
-        expected = [complex_type, enum_1]
+        expected = [complex_type, enum_1, element]
         container.filter_classes()
         self.assertEqual(expected, list(container))
 
diff --git a/tests/fixtures/dtd/models/__init__.py b/tests/fixtures/dtd/models/__init__.py
index c8bc4b42..2d6702c8 100644
--- a/tests/fixtures/dtd/models/__init__.py
+++ b/tests/fixtures/dtd/models/__init__.py
@@ -1,13 +1,23 @@
 from tests.fixtures.dtd.models.complete_example import (
     Blog,
+    Body,
+    Origin,
     Post,
     PostStatus,
+    Source,
+    Tag,
     Tags,
+    Title,
 )
 
 __all__ = [
     "Blog",
+    "Body",
+    "Origin",
     "Post",
     "PostStatus",
+    "Source",
+    "Tag",
     "Tags",
+    "Title",
 ]
diff --git a/tests/fixtures/dtd/models/complete_example.py b/tests/fixtures/dtd/models/complete_example.py
index 47fadb86..edeeba95 100644
--- a/tests/fixtures/dtd/models/complete_example.py
+++ b/tests/fixtures/dtd/models/complete_example.py
@@ -3,14 +3,64 @@ from enum import Enum
 from typing import List, Optional
 
 
+@dataclass
+class Body:
+    value: str = field(
+        default="",
+        metadata={
+            "required": True,
+        },
+    )
+
+
+@dataclass
+class Origin:
+    value: str = field(
+        default="",
+        metadata={
+            "required": True,
+        },
+    )
+
+
 class PostStatus(Enum):
     DRAFT = "draft"
     PUBLISHED = "published"
 
 
+@dataclass
+class Source:
+    value: str = field(
+        default="",
+        metadata={
+            "required": True,
+        },
+    )
+
+
+@dataclass
+class Tag:
+    value: str = field(
+        default="",
+        metadata={
+            "required": True,
+        },
+    )
+
+
+@dataclass
+class Title:
+    value: str = field(
+        default="",
+        metadata={
+            "required": True,
+        },
+    )
+
+
 @dataclass
 class Tags:
-    tag: List[str] = field(
+    tag: List[Tag] = field(
         default_factory=list,
         metadata={
             "name": "Tag",
@@ -50,21 +100,21 @@ class Post:
             "required": True,
         },
     )
-    origin: List[str] = field(
+    origin: List[Origin] = field(
         default_factory=list,
         metadata={
             "name": "Origin",
             "type": "Element",
         },
     )
-    source: List[str] = field(
+    source: List[Source] = field(
         default_factory=list,
         metadata={
             "name": "Source",
             "type": "Element",
         },
     )
-    title: Optional[str] = field(
+    title: Optional[Title] = field(
         default=None,
         metadata={
             "name": "Title",
@@ -72,7 +122,7 @@ class Post:
             "required": True,
         },
     )
-    body: Optional[str] = field(
+    body: Optional[Body] = field(
         default=None,
         metadata={
             "name": "Body",
diff --git a/tests/fixtures/primer/order.py b/tests/fixtures/primer/order.py
index 1658a28b..c200987c 100644
--- a/tests/fixtures/primer/order.py
+++ b/tests/fixtures/primer/order.py
@@ -4,6 +4,73 @@ from typing import List, Optional
 from xsdata.models.datatype import XmlDate
 
 
+@dataclass
+class Usaddress:
+    class Meta:
+        name = "USAddress"
+
+    name: Optional[str] = field(
+        default=None,
+        metadata={
+            "type": "Element",
+            "namespace": "",
+            "required": True,
+        },
+    )
+    street: Optional[str] = field(
+        default=None,
+        metadata={
+            "type": "Element",
+            "namespace": "",
+            "required": True,
+        },
+    )
+    city: Optional[str] = field(
+        default=None,
+        metadata={
+            "type": "Element",
+            "namespace": "",
+            "required": True,
+        },
+    )
+    state: Optional[str] = field(
+        default=None,
+        metadata={
+            "type": "Element",
+            "namespace": "",
+            "required": True,
+        },
+    )
+    zip: Optional[Decimal] = field(
+        default=None,
+        metadata={
+            "type": "Element",
+            "namespace": "",
+            "required": True,
+        },
+    )
+    country: str = field(
+        init=False,
+        default="US",
+        metadata={
+            "type": "Attribute",
+        },
+    )
+
+
+@dataclass
+class Comment:
+    class Meta:
+        name = "comment"
+
+    value: str = field(
+        default="",
+        metadata={
+            "required": True,
+        },
+    )
+
+
 @dataclass
 class Items:
     item: List["Items.Item"] = field(
@@ -56,7 +123,7 @@ class Items:
                 "required": True,
             },
         )
-        comment: Optional[str] = field(
+        comment: Optional[Comment] = field(
             default=None,
             metadata={
                 "type": "Element",
@@ -81,73 +148,6 @@ class Items:
         )
 
 
-@dataclass
-class Usaddress:
-    class Meta:
-        name = "USAddress"
-
-    name: Optional[str] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-            "required": True,
-        },
-    )
-    street: Optional[str] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-            "required": True,
-        },
-    )
-    city: Optional[str] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-            "required": True,
-        },
-    )
-    state: Optional[str] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-            "required": True,
-        },
-    )
-    zip: Optional[Decimal] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-            "required": True,
-        },
-    )
-    country: str = field(
-        init=False,
-        default="US",
-        metadata={
-            "type": "Attribute",
-        },
-    )
-
-
-@dataclass
-class Comment:
-    class Meta:
-        name = "comment"
-
-    value: str = field(
-        default="",
-        metadata={
-            "required": True,
-        },
-    )
-
-
 @dataclass
 class PurchaseOrderType:
     """Purchase order schema for Example.com.
@@ -183,7 +183,7 @@ class PurchaseOrderType:
             "required": True,
         },
     )
-    comment: Optional[str] = field(
+    comment: Optional[Comment] = field(
         default=None,
         metadata={
             "type": "Element",
diff --git a/tests/fixtures/primer/sample.json b/tests/fixtures/primer/sample.json
index c1252a05..b77907a4 100644
--- a/tests/fixtures/primer/sample.json
+++ b/tests/fixtures/primer/sample.json
@@ -15,14 +15,18 @@
     "zip": "95819",
     "country": "US"
   },
-  "comment": "Hurry, my lawn is going wild!",
+  "comment": {
+    "value": "Hurry, my lawn is going wild!"
+  },
   "items": {
     "item": [
       {
         "productName": "Lawnmower",
         "quantity": 1,
         "USPrice": "148.95",
-        "comment": "Confirm this is electric",
+        "comment": {
+          "value": "Confirm this is electric"
+        },
         "shipDate": null,
         "partNum": "872-AA"
       },
diff --git a/tests/models/test_config.py b/tests/models/test_config.py
index c8247b11..35423072 100644
--- a/tests/models/test_config.py
+++ b/tests/models/test_config.py
@@ -33,7 +33,6 @@ class GeneratorConfigTests(TestCase):
             '    <Format repr="true" eq="true" order="false" unsafeHash="false" frozen="false" slots="false" kwOnly="false">dataclasses</Format>\n'
             "    <Structure>filenames</Structure>\n"
             "    <DocstringStyle>reStructuredText</DocstringStyle>\n"
-            "    <FilterStrategy>allGlobals</FilterStrategy>\n"
             "    <RelativeImports>false</RelativeImports>\n"
             '    <CompoundFields defaultName="choice" useSubstitutionGroups="false" forceDefaultName="false" maxNameParts="3">false</CompoundFields>\n'
             "    <PostponedAnnotations>false</PostponedAnnotations>\n"
@@ -94,7 +93,6 @@ class GeneratorConfigTests(TestCase):
             ' frozen="false" slots="false" kwOnly="false">dataclasses</Format>\n'
             "    <Structure>filenames</Structure>\n"
             "    <DocstringStyle>reStructuredText</DocstringStyle>\n"
-            "    <FilterStrategy>allGlobals</FilterStrategy>\n"
             "    <RelativeImports>false</RelativeImports>\n"
             '    <CompoundFields defaultName="choice" useSubstitutionGroups="false" forceDefaultName="false" maxNameParts="3">false</CompoundFields>\n'
             "    <PostponedAnnotations>false</PostponedAnnotations>\n"