Repository: koxudaxi/datamodel-code-generator

Issue:
oneOf with subschema array items not incorporated/generated as Any for pydantic.v2
**Describe the bug**
When using a JSON schema as input with aoneOf construct where one option is an array with items defined in a subschema, the resulting pydantic v2 model does not incorporate the subschema definition, but rather `list[Any]`

**To Reproduce**

The following JSON schema snippet:
```json
    "SpatialPlan": {
      "type": "object",
      "properties": {
        "officialDocument": {
          "title": "officialDocument",
          "description": "Link to the official documents that relate to the spatial plan.",
          "oneOf": [
            {
              "$ref": "definitions/voidable.json#/definitions/Voidable"
            },
            {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "definitions/ref.json#/definitions/FeatureRef"
              },
              "uniqueItems": true
            }
          ]
        }

```
leads to the pydantic v2 model:

```python
class OfficialDocument(RootModel[list[Any]]):
    root: Annotated[
        list[Any],
        Field(
            description='Link to the official documents that relate to the spatial plan.',
            min_length=1,
            title='officialDocument',
        ),
    ]

class SpatialPlan(BaseModel):
    officialDocument: Annotated[
        Voidable | OfficialDocument,
        Field(
            description='Link to the official documents that relate to the spatial plan.',
            title='officialDocument',
        ),
    ]
```

Used commandline:
```
$ datamodel-codegen --target-python-version 3.10 --use-union-operator --use-standard-collections --use-schema-description --use-annotated --collapse-root-models --output-model-type pydantic_v2.BaseModel --input input.json --output output.py
```

**Expected behavior**
The resulting pydantic model should look like this:
```python
class OfficialDocument(RootModel[list[FeatureRef]]):
    root: Annotated[
        list[FeatureRef],
        Field(
            description="Link to the official documents that relate to the spatial plan.",
            min_length=1,
            title="officialDocument",
        ),
    ]
```
Or maybe even more preferable, the addtional RootModel definition should be dropped as a whole:
```python
class SpatialPlan(BaseModel):
    officialDocument: Annotated[
        list[FeatureRef] | Voidable,
        Field(
            description="Link to the official documents that relate to the spatial plan.",
            min_length=1,
            title="officialDocument",
        ),
    ]
```

**Version:**
 - OS: Ubuntu 22.04 (WSL)
 - Python version: 3.10
 - datamodel-code-generator version: 0.25.5

**Additional context**
Add any other context about the problem here.



Discussion:
Is this related to: https://github.com/koxudaxi/datamodel-code-generator/blob/fcab9a4d555d4b96d64bb277f974bb7507982fb2/datamodel_code_generator/parser/jsonschema.py#L681-L694 

If so - or if you can provide another hint - maybe we can have a look and work on a PR. This issue is really hampering our use case.
I've been looking into a similar issue on my project - so far I think it may be related to enabling the `--field-constraints` option, which is also enabled by using the `--use-annotated` option.  

I'm working off of a very slightly modified version of the [CycloneDX 1.5 schema](https://cyclonedx.org/docs/1.5/json/#tab-pane_components_items_licenses_oneOf_i1), where the `licenses` field here is changed from an `array` to `object` type (due to some other issue with datamodel-code-generator parsing the schema).  I expect to get a Python class somewhere that includes the `expression` and `bom-ref` fields.  Here's what I'm seeing using datamodel-codegen 0.25.6, with the command 

`datamodel-codegen --input ~/temp/modified-bom-1.5.schema.json --output output-license-obj-annotated --use-annot
ated`:

``` 
class LicenseChoice1(BaseModel):
    __root__: Annotated[
        List[Any],
        Field(
            description='A tuple of exactly one SPDX License Expression.',
            max_items=1,
            min_items=1,
            title='SPDX License Expression',
        ),
    ]

class LicenseChoice(BaseModel):
    __root__: Annotated[
        Union[List[LicenseChoiceItem], LicenseChoice1],
        Field(
            description='EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression)',
            title='License Choice',
        ),
    ]
```

When I remove `--use-annotated`, I get something more like what I expect:

```
class LicenseChoiceItem1(BaseModel):
    class Config:
        extra = Extra.forbid

    expression: str = Field(
        ...,
        examples=[
            'Apache-2.0 AND (MIT OR GPL-2.0-only)',
            'GPL-3.0-only WITH Classpath-exception-2.0',
        ],
        title='SPDX License Expression',
    )
    bom_ref: Optional[RefType] = Field(
        None,
        alias='bom-ref',
        description='An optional identifier which can be used to reference the license elsewhere in the BOM. Every bom-ref MUST be unique within the BOM.',
        title='BOM Reference',
    )

class LicenseChoice(BaseModel):
    __root__: Union[List[LicenseChoiceItem], List[LicenseChoiceItem1]] = Field(
        ...,
        description='EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression)',
        title='License Choice',
    )
```

I'll keep digging, but for now it appears that using annotations/field constraints ends up dropping type information somewhere down that path.

Resolving patch:
diff --git a/datamodel_code_generator/parser/jsonschema.py b/datamodel_code_generator/parser/jsonschema.py
--- a/datamodel_code_generator/parser/jsonschema.py
+++ b/datamodel_code_generator/parser/jsonschema.py
@@ -1311,6 +1311,10 @@ def parse_root_type(
             data_type = self.data_type_manager.get_data_type_from_full_path(
                 obj.custom_type_path, is_custom_type=True
             )
+        elif obj.is_array:
+            data_type = self.parse_array_fields(
+                name, obj, get_special_path('array', path)
+            ).data_type
         elif obj.anyOf or obj.oneOf:
             reference = self.model_resolver.add(
                 path, name, loaded=True, class_name=True


Test patch:
diff --git a/tests/data/expected/main/main_one_of_with_sub_schema_array_item/output.py b/tests/data/expected/main/main_one_of_with_sub_schema_array_item/output.py
new file mode 100644
--- /dev/null
+++ b/tests/data/expected/main/main_one_of_with_sub_schema_array_item/output.py
@@ -0,0 +1,17 @@
+# generated by datamodel-codegen:
+#   filename:  one_of_with_sub_schema_array_item.json
+#   timestamp: 2019-07-26T00:00:00+00:00
+
+from __future__ import annotations
+
+from typing import List, Optional, Union
+
+from pydantic import AnyUrl, BaseModel, Field
+
+
+class SpatialPlan(BaseModel):
+    officialDocument: Optional[Union[str, List[AnyUrl]]] = Field(
+        None,
+        description='Link to the official documents that relate to the spatial plan.',
+        title='officialDocument',
+    )
diff --git a/tests/data/jsonschema/one_of_with_sub_schema_array_item.json b/tests/data/jsonschema/one_of_with_sub_schema_array_item.json
new file mode 100644
--- /dev/null
+++ b/tests/data/jsonschema/one_of_with_sub_schema_array_item.json
@@ -0,0 +1,25 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "SpatialPlan",
+  "type": "object",
+  "properties": {
+    "officialDocument": {
+      "title": "officialDocument",
+      "description": "Link to the official documents that relate to the spatial plan.",
+      "oneOf": [
+        {
+          "type": "string"
+        },
+        {
+          "type": "array",
+          "minItems": 1,
+          "items": {
+            "type": "string",
+            "format": "uri"
+          },
+          "uniqueItems": true
+        }
+      ]
+    }
+  }
+}
diff --git a/tests/test_main.py b/tests/test_main.py
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -6798,3 +6798,30 @@ def test_main_root_one_of():
                 path.relative_to(expected_directory)
             ).read_text()
             assert result == path.read_text()
+
+
+@freeze_time('2019-07-26')
+def test_one_of_with_sub_schema_array_item():
+    with TemporaryDirectory() as output_dir:
+        output_file: Path = Path(output_dir) / 'output.py'
+        return_code: Exit = main(
+            [
+                '--input',
+                str(JSON_SCHEMA_DATA_PATH / 'one_of_with_sub_schema_array_item.json'),
+                '--output',
+                str(output_file),
+                '--input-file-type',
+                'jsonschema',
+                '--output-model-type',
+                'pydantic_v2.BaseModel',
+            ]
+        )
+        assert return_code == Exit.OK
+        assert (
+            output_file.read_text()
+            == (
+                EXPECTED_MAIN_PATH
+                / 'main_one_of_with_sub_schema_array_item'
+                / 'output.py'
+            ).read_text()
+        )