Repository: cloudevents/sdk-python

Issue:
to_json on a pydantic.CloudEvent does not include extension values
## Expected Behavior
When running the following:
```python
from cloudevents.conversion import to_json
from cloudevents.http import CloudEvent as HttpCloudEvent
from cloudevents.pydantic import CloudEvent as PydanticCloudEvent

data = {"data-key": "val"}
attributes = {
    "type": "com.example.string",
    "source": "https://example.com/event-producer",
    "extension-key": "test-value",
}

pydantic_event = PydanticCloudEvent(attributes, data)
http_event = HttpCloudEvent(attributes, data)

print(f'Pydantic Event JSON:\n{to_json(pydantic_event)}\n')
print(f'HTTP Event JSON:\n{to_json(http_event)}\n')
```

the following should be output:
```
Pydantic Event JSON:
b'{"specversion": "1.0", "id": "fb848780-bf68-48db-8497-efbedacc495f", "source": "https://example.com/event-producer", "type": "com.example.string", "time": "2023-10-25T13:46:20.207850+00:00", "data": {"data-key": "val"}, "extension-key": "test-value"}}'

HTTP Event JSON:
b'{"specversion": "1.0", "id": "49c6a9a8-c640-428f-ad1a-dce111008cdf", "source": "https://example.com/event-producer", "type": "com.example.string", "time": "2023-10-25T13:46:20.208740+00:00", "data": {"data-key": "val"}, "extension-key": "test-value"}'
```

## Actual Behavior
This is the output:

```
Pydantic Event JSON:
b'{"specversion": "1.0", "id": "fb848780-bf68-48db-8497-efbedacc495f", "source": "https://example.com/event-producer", "type": "com.example.string", "time": "2023-10-25T13:46:20.207850+00:00", "data": {"data-key": "val"}}'

HTTP Event JSON:
b'{"specversion": "1.0", "id": "49c6a9a8-c640-428f-ad1a-dce111008cdf", "source": "https://example.com/event-producer", "type": "com.example.string", "time": "2023-10-25T13:46:20.208740+00:00", "data": {"data-key": "val"}, "extension-key": "test-value"}'
```

Note the missing `extension-key` value from the Pydantic Event JSON.

## Steps to Reproduce the Problem

1. Run the above code block

## Specifications

- Platform: Mac
- Python Version: 3.11.5
- cloudevents Python SDK version: 1.10.0

Discussion:

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1169a0a..15ab654 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -11,12 +11,12 @@ repos:
       - id: isort
         args: [ "--profile", "black", "--filter-files" ]
   - repo: https://github.com/psf/black
-    rev: 23.9.1
+    rev: 23.10.1
     hooks:
       - id: black
-        language_version: python3.10
+        language_version: python3.11
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: "v1.6.0"
+    rev: v1.6.1
     hooks:
       - id: mypy
         files: ^(cloudevents/)
@@ -24,4 +24,4 @@ repos:
         types: [ python ]
         args: [ ]
         additional_dependencies:
-          - 'pydantic'
+          - "pydantic"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44e991b..51fcb0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+## [1.10.1]
+
+### Fixed
+- Fixed Pydantic v2 `to_json` (and `to_structured`) conversion ([#229])
+
 ## [1.10.0] — 2023-09-25
 ### Added
 - Pydantic v2 support. ([#219])
@@ -185,6 +190,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 - Initial release
 
+[1.10.1]: https://github.com/cloudevents/sdk-python/compare/1.10.0...1.10.1
 [1.10.0]: https://github.com/cloudevents/sdk-python/compare/1.9.0...1.10.0
 [1.9.0]: https://github.com/cloudevents/sdk-python/compare/1.8.0...1.9.0
 [1.8.0]: https://github.com/cloudevents/sdk-python/compare/1.7.0...1.8.0
@@ -266,3 +272,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 [#218]: https://github.com/cloudevents/sdk-python/pull/218
 [#219]: https://github.com/cloudevents/sdk-python/pull/219
 [#221]: https://github.com/cloudevents/sdk-python/pull/221
+[#229]: https://github.com/cloudevents/sdk-python/pull/229
diff --git a/cloudevents/__init__.py b/cloudevents/__init__.py
index 1cabc33..c6e1151 100644
--- a/cloudevents/__init__.py
+++ b/cloudevents/__init__.py
@@ -12,4 +12,4 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-__version__ = "1.10.0"
+__version__ = "1.10.1"
diff --git a/cloudevents/pydantic/v1/event.py b/cloudevents/pydantic/v1/event.py
index cd38701..d18736a 100644
--- a/cloudevents/pydantic/v1/event.py
+++ b/cloudevents/pydantic/v1/event.py
@@ -186,7 +186,7 @@ class CloudEvent(abstract.CloudEvent, BaseModel):  # type: ignore
                 )
             attributes = {k.lower(): v for k, v in attributes.items()}
             kwargs.update(attributes)
-        super(CloudEvent, self).__init__(data=data, **kwargs)
+        super().__init__(data=data, **kwargs)
 
     class Config:
         extra: str = "allow"  # this is the way we implement extensions
diff --git a/cloudevents/pydantic/v2/event.py b/cloudevents/pydantic/v2/event.py
index 17ed8d9..4ae8bb5 100644
--- a/cloudevents/pydantic/v2/event.py
+++ b/cloudevents/pydantic/v2/event.py
@@ -134,7 +134,7 @@ class CloudEvent(abstract.CloudEvent, BaseModel):  # type: ignore
                 )
             attributes = {k.lower(): v for k, v in attributes.items()}
             kwargs.update(attributes)
-        super(CloudEvent, self).__init__(data=data, **kwargs)
+        super().__init__(data=data, **kwargs)
 
     model_config = ConfigDict(
         extra="allow",  # this is the way we implement extensions
@@ -209,7 +209,7 @@ class CloudEvent(abstract.CloudEvent, BaseModel):  # type: ignore
     def _get_attributes(self) -> typing.Dict[str, typing.Any]:
         return {
             key: conversion.best_effort_encode_attribute_value(value)
-            for key, value in self.__dict__.items()
+            for key, value in dict(BaseModel.__iter__(self)).items()
             if key not in ["data"]
         }
 


Test patch:
diff --git a/cloudevents/tests/test_pydantic_conversions.py b/cloudevents/tests/test_pydantic_conversions.py
index 4beb981..801b76b 100644
--- a/cloudevents/tests/test_pydantic_conversions.py
+++ b/cloudevents/tests/test_pydantic_conversions.py
@@ -33,9 +33,9 @@ test_data = json.dumps({"data-key": "val"})
 test_attributes = {
     "type": "com.example.string",
     "source": "https://example.com/event-producer",
+    "extension-attribute": "extension-attribute-test-value",
 }
 
-
 _pydantic_implementation = {
     "v1": {
         "event": PydanticV1CloudEvent,