Repository: pandas-dev/pandas

Issue:
read_json with table='orient' causes unexpected type coercion
#### Code Sample, a copy-pastable example if possible

```python
import pandas as pd

df = pd.DataFrame([[1., 2., 3.], [1., 2., 3.], [1., 2., 3.]], columns=['a', 'b', 'c'])
out = df.to_json(orient='table')
result = pd.read_json(out, orient='table')
pd.testing.assert_frame_equal(df, result)

```
#### Problem description

Assertion fails with error:
```
Attribute "dtype" are different
[left]:  float64
[right]: int64
```
Error is related to this method: https://github.com/pandas-dev/pandas/blob/master/pandas/io/json/json.py#L659

#### Expected Output

Expected result is passing assertion. Original DF and the JSON round-tripped DF should have same dtype.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: 2461b90a14b7e9276e06be09aa96a9cbf8f39686
python: 3.6.4.final.0
python-bits: 64
OS: Darwin
OS-release: 16.7.0
machine: x86_64
processor: i386
byteorder: little
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.24.0.dev0+72.g2461b90a1.dirty
pytest: 3.6.0
pip: 9.0.1
setuptools: 28.8.0
Cython: 0.28.3
numpy: 1.14.3
scipy: 1.1.0
pyarrow: 0.9.0.post1
xarray: 0.10.6
IPython: 6.4.0
sphinx: 1.7.5
patsy: None
dateutil: 2.7.3
pytz: 2018.4
blosc: 1.5.1
bottleneck: 1.2.1
tables: 3.4.3
numexpr: 2.6.5
feather: 0.4.0
matplotlib: 2.2.2
openpyxl: 2.5.3
xlrd: 1.1.0
xlwt: 1.3.0
xlsxwriter: 1.0.5
lxml: 4.2.1
bs4: 4.6.0
html5lib: 1.0.1
sqlalchemy: 1.2.8
pymysql: 0.8.1
psycopg2: None
jinja2: 2.10
s3fs: 0.1.5
fastparquet: 0.1.5
pandas_gbq: None
pandas_datareader: None

</details>



Discussion:
xref https://github.com/pandas-dev/pandas/pull/21318#discussion_r193223127

Resolving patch:
diff --git a/pandas/io/json/json.py b/pandas/io/json/json.py
--- a/pandas/io/json/json.py
+++ b/pandas/io/json/json.py
@@ -226,7 +226,7 @@ def _write(self, obj, orient, double_precision, ensure_ascii,
         return serialized
 
 
-def read_json(path_or_buf=None, orient=None, typ='frame', dtype=True,
+def read_json(path_or_buf=None, orient=None, typ='frame', dtype=None,
               convert_axes=True, convert_dates=True, keep_default_dates=True,
               numpy=False, precise_float=False, date_unit=None, encoding=None,
               lines=False, chunksize=None, compression='infer'):
@@ -278,8 +278,15 @@ def read_json(path_or_buf=None, orient=None, typ='frame', dtype=True,
 
     typ : type of object to recover (series or frame), default 'frame'
     dtype : boolean or dict, default True
-        If True, infer dtypes, if a dict of column to dtype, then use those,
+        If True, infer dtypes; if a dict of column to dtype, then use those;
         if False, then don't infer dtypes at all, applies only to the data.
+
+        Not applicable with ``orient='table'``.
+
+        .. versionchanged:: 0.25
+
+           Not applicable with ``orient='table'``.
+
     convert_axes : boolean, default True
         Try to convert the axes to the proper dtypes.
     convert_dates : boolean, default True
@@ -408,6 +415,11 @@ def read_json(path_or_buf=None, orient=None, typ='frame', dtype=True,
                 {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
     """
 
+    if orient == 'table' and dtype:
+        raise ValueError("cannot pass both dtype and orient='table'")
+
+    dtype = orient != 'table' if dtype is None else dtype
+
     compression = _infer_compression(path_or_buf, compression)
     filepath_or_buffer, _, compression, should_close = get_filepath_or_buffer(
         path_or_buf, encoding=encoding, compression=compression,
@@ -600,15 +612,15 @@ class Parser(object):
         'us': long(31536000000000),
         'ns': long(31536000000000000)}
 
-    def __init__(self, json, orient, dtype=True, convert_axes=True,
+    def __init__(self, json, orient, dtype=None, convert_axes=True,
                  convert_dates=True, keep_default_dates=False, numpy=False,
                  precise_float=False, date_unit=None):
         self.json = json
 
         if orient is None:
             orient = self._default_orient
-
         self.orient = orient
+
         self.dtype = dtype
 
         if orient == "split":


Test patch:
diff --git a/pandas/tests/io/json/test_json_table_schema.py b/pandas/tests/io/json/test_json_table_schema.py
--- a/pandas/tests/io/json/test_json_table_schema.py
+++ b/pandas/tests/io/json/test_json_table_schema.py
@@ -502,12 +502,12 @@ class TestTableOrientReader(object):
     @pytest.mark.parametrize("vals", [
         {'ints': [1, 2, 3, 4]},
         {'objects': ['a', 'b', 'c', 'd']},
+        {'objects': ['1', '2', '3', '4']},
         {'date_ranges': pd.date_range('2016-01-01', freq='d', periods=4)},
         {'categoricals': pd.Series(pd.Categorical(['a', 'b', 'c', 'c']))},
         {'ordered_cats': pd.Series(pd.Categorical(['a', 'b', 'c', 'c'],
                                                   ordered=True))},
-        pytest.param({'floats': [1., 2., 3., 4.]},
-                     marks=pytest.mark.xfail),
+        {'floats': [1., 2., 3., 4.]},
         {'floats': [1.1, 2.2, 3.3, 4.4]},
         {'bools': [True, False, False, True]}])
     def test_read_json_table_orient(self, index_nm, vals, recwarn):
diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py
--- a/pandas/tests/io/json/test_pandas.py
+++ b/pandas/tests/io/json/test_pandas.py
@@ -1202,6 +1202,21 @@ def test_data_frame_size_after_to_json(self):
 
         assert size_before == size_after
 
+    def test_from_json_to_json_table_dtypes(self):
+        # GH21345
+        expected = pd.DataFrame({'a': [1, 2], 'b': [3., 4.], 'c': ['5', '6']})
+        dfjson = expected.to_json(orient='table')
+        result = pd.read_json(dfjson, orient='table')
+        assert_frame_equal(result, expected)
+
+    @pytest.mark.parametrize('dtype', [True, {'b': int, 'c': int}])
+    def test_read_json_table_dtype_raises(self, dtype):
+        # GH21345
+        df = pd.DataFrame({'a': [1, 2], 'b': [3., 4.], 'c': ['5', '6']})
+        dfjson = df.to_json(orient='table')
+        with pytest.raises(ValueError):
+            pd.read_json(dfjson, orient='table', dtype=dtype)
+
     @pytest.mark.parametrize('data, expected', [
         (DataFrame([[1, 2], [4, 5]], columns=['a', 'b']),
             {'columns': ['a', 'b'], 'data': [[1, 2], [4, 5]]}),