Repository: litestar-org/polyfactory (python)

Issue:
Bug: Pydantic Json type is disregarded when creating mock data
### Description

It seems like the pydantic_factory completely disregards a given [Json](https://docs.pydantic.dev/latest/usage/types/json/) type for a field if it has any nesting.

I ran into the problem today while trying to create test with a more nested model, but realized it's reproduceable with the simplest of examples. 

From a small investigation, it seems that the `FieldInfo.metadata` parsing is lacking and is just completely disregarding the Json type if found in the metadata.

https://github.com/litestar-org/polyfactory/blob/f54f9f3c0b849a2d06bf4a5332da80ccdc31d1a9/polyfactory/factories/pydantic_factory.py#L89-L92

And I guess the parse_constrraints function itself

https://github.com/litestar-org/polyfactory/blob/f54f9f3c0b849a2d06bf4a5332da80ccdc31d1a9/polyfactory/field_meta.py#L162-L207

I contemplated trying to fix it but at least for now I don't really know how to proceed.

### MCVE

```python
from pydantic import BaseModel, Json
from polyfactory.factories.pydantic_factory import ModelFactory

class BasicTestModel(BaseModel):
    body: Json[int]


class BasicTestFactory(ModelFactory):
    __model__ = BasicTestModel


basicTest = BasicTestFactory.build()
```


### Steps to reproduce

```bash
1. Create a Model with a Json field.
2. Create a Factory for the model.
3. Try to build a mock model with the factory.
```

### Logs

```bash
Traceback (most recent call last):
  File ".../polyfactory-json/test.py", line 40, in <module>
    basicTest = BasicTestFactory.build()
                ^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../polyfactory/factories/pydantic_factory.py", line 310, in build
    return cls.__model__(**processed_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../pydantic/main.py", line 150, in __init__
    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for BasicTestModel
body
  JSON input should be string, bytes or bytearray [type=json_type, input_value=6014, input_type=int]
    For further information visit https://errors.pydantic.dev/2.1.2/v/json_type
```


### Litestar Version

-

### Platform

- [X] Linux
- [X] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)

Pull request:
fix: add support for Json type in Pydantic factory
[//]: # "By submitting this pull request, you agree to:"
[//]: # "- follow [Litestar's Code of Conduct](https://github.com/litestar-org/.github/blob/main/CODE_OF_CONDUCT.md)"
[//]: # "- follow [Litestar's contribution guidelines](https://github.com/litestar-org/.github/blob/main/CONTRIBUTING.md)"

### Pull Request Checklist

- [x] New code has 100% test coverage
- [ ] (If applicable) The prose documentation has been updated to reflect the changes introduced by this PR
- [ ] (If applicable) The reference documentation has been updated to reflect the changes introduced by this PR
- [x] Pre-Commit Checks were ran and passed
- [x] Tests were ran and passed

### Description
[//]: # "Please describe your pull request for new release changelog purposes"

- add support for `Json` type in Pydantic factory

### Close Issue(s)
[//]: # "Please add in issue numbers this pull request will close, if applicable"
[//]: # "Examples: Fixes #4321 or Closes #1234"

- resolves #302 


Patch:
diff --git a/polyfactory/factories/pydantic_factory.py b/polyfactory/factories/pydantic_factory.py
index 9216644..3ea3ba3 100644
--- a/polyfactory/factories/pydantic_factory.py
+++ b/polyfactory/factories/pydantic_factory.py
@@ -29,8 +29,9 @@ from polyfactory.utils.helpers import unwrap_new_type, unwrap_optional
 from polyfactory.utils.predicates import is_optional_union, is_safe_subclass, is_union
 
 try:
-    from pydantic import VERSION, BaseModel
+    from pydantic import VERSION, BaseModel, Json
     from pydantic.fields import FieldInfo
+    from pydantic_core import to_json
 except ImportError as e:
     raise MissingDependencyException("pydantic is not installed") from e
 
@@ -44,14 +45,34 @@ except ImportError:
 if TYPE_CHECKING:
     from random import Random
 
-    from typing_extensions import TypeGuard
+    from typing_extensions import NotRequired, TypeGuard
 
 T = TypeVar("T", bound=BaseModel)
 
 
+class PydanticConstraints(Constraints):
+    """Metadata regarding a Pydantic type constraints, if any"""
+
+    json: NotRequired[bool]
+
+
 class PydanticFieldMeta(FieldMeta):
     """Field meta subclass capable of handling pydantic ModelFields"""
 
+    def __init__(
+        self,
+        *,
+        name: str,
+        annotation: type,
+        random: Random | None = None,
+        default: Any = ...,
+        children: list[FieldMeta] | None = None,
+        constraints: PydanticConstraints | None = None,
+    ) -> None:
+        super().__init__(
+            name=name, annotation=annotation, random=random, default=default, children=children, constraints=constraints
+        )
+
     @classmethod
     def from_field_info(
         cls,
@@ -102,13 +123,23 @@ class PydanticFieldMeta(FieldMeta):
                 for arg in get_args(annotation)
             ]
 
-        if metadata := [v for v in field_info.metadata if v is not None]:
-            constraints = cls.parse_constraints(metadata=metadata)
+        metadata, is_json = [], False
+        for m in field_info.metadata:
+            if not is_json and isinstance(m, Json):  # type: ignore[misc]
+                is_json = True
+            elif m is not None:
+                metadata.append(m)
+
+        constraints = cls.parse_constraints(metadata=metadata) if metadata else {}
+        constraints = cast(PydanticConstraints, constraints)
 
         if "url" in constraints:
             # pydantic uses a sentinel value for url constraints
             annotation = str
 
+        if is_json:
+            constraints["json"] = True
+
         return PydanticFieldMeta.from_type(
             annotation=annotation,
             children=children,
@@ -245,7 +276,7 @@ class PydanticFieldMeta(FieldMeta):
             annotation=annotation,
             children=children or None,
             default=default_value,
-            constraints=cast("Constraints", {k: v for k, v in constraints.items() if v is not None}) or None,
+            constraints=cast("PydanticConstraints", {k: v for k, v in constraints.items() if v is not None}) or None,
         )
 
 
@@ -311,6 +342,15 @@ class ModelFactory(Generic[T], BaseFactory[T]):
                 ]
         return cls._fields_metadata
 
+    @classmethod
+    def get_constrained_field_value(cls, annotation: Any, field_meta: FieldMeta) -> Any:
+        constraints = cast(PydanticConstraints, field_meta.constraints)
+        if constraints.pop("json", None):
+            value = cls.get_field_value(field_meta)
+            return to_json(value)
+
+        return super().get_constrained_field_value(annotation, field_meta)
+
     @classmethod
     def build(cls, factory_use_construct: bool = False, **kwargs: Any) -> T:
         """Build an instance of the factory's __model__


Test patch:
diff --git a/tests/test_pydantic_factory.py b/tests/test_pydantic_factory.py
index bd4b784..153cb37 100644
--- a/tests/test_pydantic_factory.py
+++ b/tests/test_pydantic_factory.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 import sys
 
 import pytest
-from pydantic import VERSION, BaseModel, Field
+from pydantic import VERSION, BaseModel, Field, Json
 
 from polyfactory.factories.pydantic_factory import ModelFactory
 
@@ -39,3 +39,28 @@ def test_list_unions() -> None:
     assert isinstance(CFactory.build().c, list)
     assert len(CFactory.build().c) > 0
     assert isinstance(CFactory.build().c[0], (A, B))
+
+
+@pytest.mark.skipif(VERSION.startswith("1"), reason="only for Pydantic v2")
+def test_json_type() -> None:
+    class A(BaseModel):
+        a: Json[int]
+
+    class AFactory(ModelFactory[A]):
+        __model__ = A
+
+    assert isinstance(AFactory.build(), A)
+
+
+@pytest.mark.skipif(VERSION.startswith("1"), reason="only for Pydantic v2")
+def test_nested_json_type() -> None:
+    class A(BaseModel):
+        a: int
+
+    class B(BaseModel):
+        b: Json[A]
+
+    class BFactory(ModelFactory[B]):
+        __model__ = B
+
+    assert isinstance(BFactory.build(), B)