Repository: quantumlib/Cirq

Issue:
It's too late to deprecate others using `cirq_type` in their json
There's a bad deprecation warning in the JSON serialization:

```
def _json_dict_with_cirq_type(obj: Any):
    base_dict = obj._json_dict_()
    if 'cirq_type' in base_dict:
        # TODO: upgrade to ValueError in v0.15
        warnings.warn(
            f"Found 'cirq_type': '{base_dict['cirq_type']}' in _json_dict_. "
            f"Custom values of this field are not permitted, and will produce "
            "an error starting in Cirq v0.15.",
            DeprecationWarning,
        )
        return base_dict
    return {'cirq_type': json_cirq_type(type(obj)), **base_dict}
```

**This cannot be changed into an error**. The main guarantee provided by json serialization is that it will continue to work forever. If using this key was going to be an error, it had to start as an error when json serialization was first written. You can't change it now, because that might break things which would mean they wouldn't continue to work forever.

I am aware of specific files out there that use these values, because stimcirq uses these keys for its custom operations. I've serialized circuits that had those operations in it. I expect them to continue to parse.

You are about to make a change that would break the main guarantee provided by json serialization. DO NOT DO THIS. Remove this deprecation warning instead of moving forward with it.


Discussion:
cirq_type will still appear in the serialized file, the warning is saying that your code shouldn't put it there, but rather let the serialization framework do it for you. I don't think there's any compatibility issue with previously-serialized data. 
That would be more reasonable. That said, what you are saying is not at all clear to me from the message. It just says there will be an error. I assumed it was both on serialization and deserialization. The message also doesn't describe what action I should take to deal with the deprecation. It just says what I'm doing isn't allowed, not what to do about it.

For context, the specific case I'm worried about is stimcirq, which defines these resolvers:

```
JSON_RESOLVERS_DICT = {
    "CumulativeObservableAnnotation": CumulativeObservableAnnotation,
    "DetAnnotation": DetAnnotation,
    "MeasureAndOrResetGate": MeasureAndOrResetGate,
    "ShiftCoordsAnnotation": ShiftCoordsAnnotation,
    "SweepPauli": SweepPauli,
    "TwoQubitAsymmetricDepolarizingChannel": TwoQubitAsymmetricDepolarizingChannel,
}
JSON_RESOLVER = JSON_RESOLVERS_DICT.get
```

and has values that look like this:

```
    def _json_dict_(self) -> Dict[str, Any]:
        result = {
            'cirq_type': self.__class__.__name__,
            'parity_keys': sorted(self.parity_keys),
            'observable_index': self.observable_index,
        }
        if self.relative_keys:
            result['relative_keys'] = sorted(self.relative_keys)
        return result
```
We ran into this internally as well. I think "what to do" is remove the explicit `cirq_type` entry in the dict you are returning, and define a `_json_namespace_` method that returns a string (you are encouraged _not_ to use the empty string for this). The serialization framework will add a cirq_type field that is `{json_namespace}.{obj.__class__.__name__}`. If you do change to a non-empty json namespace, say `stim`, then you'll have to update the resolvers dict to be backwards compatibile with older data, something like:
```python
# non-namespaced resolvers, for compatibility with old data.
_JSON_RESOLVERS_DICT_OLD = {
    "CumulativeObservableAnnotation": CumulativeObservableAnnotation,
    "DetAnnotation": DetAnnotation,
    "MeasureAndOrResetGate": MeasureAndOrResetGate,
    "ShiftCoordsAnnotation": ShiftCoordsAnnotation,
    "SweepPauli": SweepPauli,
    "TwoQubitAsymmetricDepolarizingChannel": TwoQubitAsymmetricDepolarizingChannel,
}
JSON_RESOLVERS_DICT = {
    **_JSON_RESOLVERS_DICT_OLD,
    **{'stim.' + k: v for k, v in _JSON_RESOLVERS_DICT_OLD.items()},
    # Add new namespaced resolvers here only.
}
JSON_RESOLVER = JSON_RESOLVERS_DICT.get
```
This is at least how we handled the serialization changes internally. Perhaps @95-martin-orion can weigh in on whether this looks right, and also whether we can improve the deprecation message to make it more clear to people how to address it.
@maffoo has everything correct here - `cirq_type` is staying in the serialization format, it's just transitioning from user-specified to auto-generated from `_json_namespace_` + class name.

This isn't the first time someone has raised this issue though, so I'm on board with updating the deprecation message to be very explicit about what's happening. I'll send a PR later today.

Resolving patch:
diff --git a/cirq-core/cirq/protocols/json_serialization.py b/cirq-core/cirq/protocols/json_serialization.py
--- a/cirq-core/cirq/protocols/json_serialization.py
+++ b/cirq-core/cirq/protocols/json_serialization.py
@@ -283,9 +283,18 @@ def _json_dict_with_cirq_type(obj: Any):
     if 'cirq_type' in base_dict:
         # TODO: upgrade to ValueError in v0.15
         warnings.warn(
-            f"Found 'cirq_type': '{base_dict['cirq_type']}' in _json_dict_. "
-            f"Custom values of this field are not permitted, and will produce "
-            "an error starting in Cirq v0.15.",
+            f"Found 'cirq_type': '{base_dict['cirq_type']}' in user-specified _json_dict_. "
+            "'cirq_type' is now automatically generated from the class's name and its "
+            "_json_namespace_ method as `cirq_type: '[<namespace>.]<class_name>'`."
+            "\n\n"
+            "Starting in v0.15, custom 'cirq_type' values will trigger an error. "
+            "To fix this, remove 'cirq_type' from the class _json_dict_ method and "
+            "define _json_namespace_ for the class."
+            "\n\n"
+            "For backwards compatibility, third-party classes whose old 'cirq_type' value "
+            "does not match the new value must appear under BOTH values in the resolver "
+            "for that package. For details on defining custom resolvers, see the "
+            "DEFAULT_RESOLVER docstring in cirq-core/cirq/protocols/json_serialization.py.",
             DeprecationWarning,
         )
         return base_dict


Test patch: