[SPARK-3713][SQL] Uses JSON to serialize DataType objects

This PR uses JSON instead of `toString` to serialize `DataType`s. The latter is not only hard to parse but also flaky in many cases.

Since we already write schema information to Parquet metadata in the old style, we have to reserve the old `DataType` parser and ensure downward compatibility. The old parser is now renamed to `CaseClassStringParser` and moved into `object DataType`.

@JoshRosen @davis Please help review PySpark related changes, thanks!
If the jsonValue has one level, then these lines can be written like this:
```
if json_value['type'] == 'array':
  return ArrayType(json_value['element'], json_value[''containsNull''])
```
Also, it will be much easier to to like this:

```
class ArrayType:
   @classmethod
   def load_from_json(cls, json):
        return ArrayType(json['element'], json[''containsNull''])

types[json_value['type']].load_from_json(json_value)
```