Repository: tiangolo/fastapi

Issue:
Pydantic __root__ model - incorrect handling
### Describe the bug

https://pydantic-docs.helpmanual.io/usage/models/#custom-root-types
Pydantic allows to create models with only `__root__` field. In such scenario the model behaves as transparent wrapper for this single type.

When such model is used in response (request also?) fastapi does not treat it correctly and renders it as object with `__root__` field.

Object is treated correctly by pydantic itself.

### To Reproduce

```
from typing import List
from fastapi import FastAPI
from pydantic.main import BaseModel

app = FastAPI()


class RootTestClass(BaseModel):
    __root__: List[str]


@app.get("/")
async def root():
    response = RootTestClass(__root__=['a', 'b', 'c'])
    print(response.json())  # ["a", "b", "c"] so it's OK
    print(RootTestClass.schema())  # {'title': 'RootTestClass', 'type': 'array', 'items': {'type': 'string'}} this is also OK
    return response  # Wrong value in http response
```

### Expected behavior

The response should be:
```
["a", "b", "c"]
```

but at the moment is:
```
{"__root__":["a","b","c"]}
```

### Screenshots

N/A

### Environment

- OS: Linux
- FastAPI Version: 0.47.1
- Python version: Python 3.7.5

### Additional context

N/A



Discussion:
If anyone wants to submit a PR to fix this I'd be happy to review it. (I think it's worth handling this properly.)
For now created issue for `pydantic` (https://github.com/samuelcolvin/pydantic/issues/1193) as it looks like it is more broken there than here.
I wouldn't recommend using `__root__` in FastAPI. `__root__` allows using other types in Pydantic apart from things with key values, like lists.

But in FastAPI, everywhere you can use a Pydantic model you can also use what would be the (arguably?) most "Pythonic" way, using `typing`. So you can do `List[SomeModel]`. Instead of having to create a `SomeModelWrapper` that users `__root__`.

`__root__` is valid and useful in Pydantic standalone as there's no other way to achieve what it does. But in FastAPI the preferred way is to use standard types that have Pydantic models as type parameters (the thing inside `List[]`).

Given that, as it's still valid Pydantic, I would be happy to support it if someone wants to add a PR with it (as @dmontagu says).
@tiangolo I understands that `response_model=Dict[str, str]` instead of a wrapped model with `__root__` is viable, however is there a way to include an `example`, perhaps similar to the `schema_extra` section that can be attach to `response_model` ?
@tiangolo Supporting pydantic root types would allow a single validator (defined in the wrapper class) to be run on all objects of a certain type- otherwise, the validator must be specified in each object that has a child of that type (as far as I can tell- I'm new to fastAPI, please let me know if there's a better way).
as @sidmani also mentioned, I'm running into wanting the ability to be able to say: 

```python
pydantic_list_as_root.dict()
```

and the above output a dict. Rather than having to manually loop through my `List[pydantic_entity]` and call `dict()` on each one. 

However, I do appreciate what @tiangolo is trying to achieve by keeping things as pythonic as possible, but I would imagine that many if not all FastAPI implementations heavily rely on Pydantic for defining schemas. Therefore, I think it would be a great idea to embrace all/most of its capabilities. 
Yeah, I would be happy to support it if someone wants to add a PR with it.

Resolving patch:
diff --git a/fastapi/encoders.py b/fastapi/encoders.py
--- a/fastapi/encoders.py
+++ b/fastapi/encoders.py
@@ -71,6 +71,8 @@ def jsonable_encoder(
                 by_alias=by_alias,
                 skip_defaults=bool(exclude_unset or skip_defaults),
             )
+        if "__root__" in obj_dict:
+            obj_dict = obj_dict["__root__"]
         return jsonable_encoder(
             obj_dict,
             exclude_none=exclude_none,


Test patch:
diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py
--- a/tests/test_jsonable_encoder.py
+++ b/tests/test_jsonable_encoder.py
@@ -76,6 +76,10 @@ class ModelWithDefault(BaseModel):
     bla: str = "bla"
 
 
+class ModelWithRoot(BaseModel):
+    __root__: str
+
+
 @pytest.fixture(
     name="model_with_path", params=[PurePath, PurePosixPath, PureWindowsPath]
 )
@@ -158,3 +162,8 @@ def test_encode_model_with_path(model_with_path):
     else:
         expected = "/foo/bar"
     assert jsonable_encoder(model_with_path) == {"path": expected}
+
+
+def test_encode_root():
+    model = ModelWithRoot(__root__="Foo")
+    assert jsonable_encoder(model) == "Foo"