Repository: pydantic/pydantic

Issue:
Order of keys differs in schema output
### Initial Checks

- [X] I confirm that I'm using Pydantic V2

### Description

When a model is specified as a Field and the values are instantiated , the keys are output in sorted order. I would expect that the order of the keys be maintained.

See sample code below. The output of the class `Stepping` when executed standalone is 

```py 
print(Stepping(type="const", step=0.15, multi_step=True))
# type='const' step=0.15 multi_step=True
```

But when the class `Stepping` is used as a  field in another Model and the Field is instantiated then the output of the schema has the values sorted.  **_'default': {'multi_step': True, 'step': 0.15, 'type': 'const'}}},_**

```py
print(Movement.model_json_schema())

{'$defs': {'Stepping': {'properties': {'type': {'const': 'const',
     'title': 'Type'},
    'step': {'title': 'Step', 'type': 'number'},
    'multi_step': {'default': False,
     'title': 'Multi Step',
     'type': 'boolean'}},
   'required': ['type', 'step'],
   'title': 'Stepping',
   'type': 'object'}},
 'properties': {'type': {'const': 'movement',
   'default': 'movement',
   'title': 'Type'},
  'step_settings': {'allOf': [{'$ref': '#/$defs/Stepping'}],
   'default': {'multi_step': True, 'step': 0.15, 'type': 'const'}}},
 'title': 'Movement',
 'type': 'object'}
```

Is there a setting that would allow the retaining of the field order in the output? Or am i doing something wrong?

Thanks in advance.

### Example Code

```Python
from pydantic import BaseModel, Field
from typing import Literal

class Stepping(BaseModel):
    type: Literal["const"] = Field(
        ...,
    )
    step: float = Field(
        ...,
    )
    multi_step: bool = Field(
        default=False,
    )

print(Stepping(type="const", step=0.15, multi_step=True))

class Movement(BaseModel):
    type: Literal["movement"] = Field(
        default="movement"
    )
    step_settings: Stepping = Field(
        default=Stepping(type="const", step=0.15, multi_step=True)
    )

print(Movement.model_json_schema())
```


### Python, Pydantic & OS Version

```Text
pydantic version: 2.3.0
pydantic-core version: 2.6.3
pydantic-core build: profile=release pgo=false
install path: /scratch/myprograms/miniconda3/envs/pydantic-v2/lib/python3.11/site-packages/pydantic
python version: 3.11.5 | packaged by conda-forge | (main, Aug 27 2023, 03:34:09) [GCC 12.3.0]
                     platform: Linux-3.10.0-1160.45.1.el7.x86_64-x86_64-with-glibc2.17
optional deps. installed: ['typing-extensions']
```



Discussion:
update , i checked the code and the source seems to be

https://github.com/pydantic/pydantic/blob/8f3ac4e30845957d79a12e8963fa37bfa0c9b811/pydantic/json_schema.py#L439

it would be nice to have a flag that also allows non sorted schema output. 

https://github.com/pydantic/pydantic/blob/8f3ac4e30845957d79a12e8963fa37bfa0c9b811/pydantic/json_schema.py#L389

### Some reasons:
- We also generate documentation from the schema and it is better to have the documentation output mirror the input
- Order of fields in output is same as input
- In pydantic v1 the order was not sorted

Update:

[6043](https://github.com/pydantic/pydantic/pull/6043) is where the change to sorting was added. Please consider adding a flag so that the output can be unsorted too.
Hi @vigneshmanick,

Thanks for looking into this! Seems like a reasonable feature request to me. Also related is #7424, enabling sorting for `model_dump` and friends.

Are you interested in opening a PR to help with this feature request? 😄
@sydney-runkle  sure, let me give it a try.
Maybe I’m misunderstanding. The goal is to make json schemas determined to stick in the face of re-factoring. We should be sorting anything that does not have a natural order and keep the natural order on the things that do. Model fields have a natural order so they should never be sorted, if they are that’s a bug.
@adriangb are you suggesting that the alphabetical sorting of the fields `z` and `a` in [this test](https://github.com/pydantic/pydantic/pull/7767/files#diff-cbcfeba88238560f75e880d530f4cd82ab6bf90247012e8f664281941f8da1f7R5711) when the sorting logic is implemented is a bug, given that `z` is defined first in the model definition?
Well that's a new test being added right? The goal of sorting is that schema generation is deterministic, e.g. when you migrate from v2 to v3 your tests don't break because the JSON schema output is different. Or if we refactor `pydantic/json_schema.py`. Without sorting we might accidentally change the order from e.g. `{"type": "string", "enum": ["one", "two", "three"]}` to `{"enum": ["one", "two", "three"], "type": "string"}` or `{"type": "string", "enum": ["three", "two", "one"]}`. Some of this is up for debate (e.g. should we sort enum values?) but I think the model fields have a natural order (the order they are defined in code) that we try to preserve elsewhere and should try to preserve in the json shcema.
yes, the order of model fields is sorted in the output which is the main issue i have with the schema output. 

e.g from the issue,  ` 'default': {'multi_step': True, 'step': 0.15, 'type': 'const'}}},` the fields are sorted.
@sydney-runkle seems like a bug around https://github.com/pydantic/pydantic/blob/8f3ac4e30845957d79a12e8963fa37bfa0c9b811/pydantic/json_schema.py#L2227 then. Can you investigate and make a PR to fix? We may need to handle `default` as a special case...
@adriangb @vigneshmanick,

Yep, I'll look into this bug and open a fix. @vigneshmanick, if we don't sort the model fields in the schema, does that eliminate the need for your sorting flag?
Yes that works for me. 

Resolving patch:
diff --git a/pydantic/json_schema.py b/pydantic/json_schema.py
--- a/pydantic/json_schema.py
+++ b/pydantic/json_schema.py
@@ -2224,7 +2224,7 @@ def _sort_json_schema(value: JsonSchemaValue, parent_key: str | None = None) ->
     if isinstance(value, dict):
         sorted_dict: dict[str, JsonSchemaValue] = {}
         keys = value.keys()
-        if parent_key != 'properties':
+        if (parent_key != 'properties') and (parent_key != 'default'):
             keys = sorted(keys)
         for key in keys:
             sorted_dict[key] = _sort_json_schema(value[key], parent_key=key)
@@ -2232,7 +2232,7 @@ def _sort_json_schema(value: JsonSchemaValue, parent_key: str | None = None) ->
     elif isinstance(value, list):
         sorted_list: list[JsonSchemaValue] = []
         for item in value:  # type: ignore
-            sorted_list.append(_sort_json_schema(item))
+            sorted_list.append(_sort_json_schema(item, parent_key))
         return sorted_list  # type: ignore
     else:
         return value


Test patch:
diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py
--- a/tests/test_json_schema.py
+++ b/tests/test_json_schema.py
@@ -5260,7 +5260,7 @@ class Model(BaseModel):
         a: str
 
     class OuterModel(BaseModel):
-        inner: List[Model]
+        inner: List[Model] = Field(default=[Model(b=1, a='fruit')])
 
     # verify the schema contents
     # this is just to get a nicer error message / diff if it fails
@@ -5273,8 +5273,14 @@ class OuterModel(BaseModel):
                 'type': 'object',
             }
         },
-        'properties': {'inner': {'items': {'$ref': '#/$defs/Model'}, 'title': 'Inner', 'type': 'array'}},
-        'required': ['inner'],
+        'properties': {
+            'inner': {
+                'default': [{'b': 1, 'a': 'fruit'}],
+                'items': {'$ref': '#/$defs/Model'},
+                'title': 'Inner',
+                'type': 'array',
+            }
+        },
         'title': 'OuterModel',
         'type': 'object',
     }