Skip to content

Generate JSON Schema deprecated from protobuf deprecated options - #165

Merged
pkwarren merged 3 commits into
bufbuild:mainfrom
aidandj:jsonschema-deprecated
Oct 19, 2026
Merged

pkwarren merged 3 commits into
bufbuild:mainfrom
aidandj:jsonschema-deprecated

Conversation

@aidandj

@aidandj aidandj commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

JSON Schema defines a deprecated annotation keyword. Map the protobuf deprecated option onto it for messages and fields.

@CLAassistant

CLAassistant commented Sep 24, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@aidandj
aidandj force-pushed the jsonschema-deprecated branch from 5ae97d8 to 658a0ef Compare September 24, 2026 02:44
JSON Schema draft 2020-12 defines a `deprecated` annotation keyword.
Map the protobuf `deprecated` option onto it for messages and fields so
consumers can surface deprecation in IDEs and docs.
@aidandj
aidandj force-pushed the jsonschema-deprecated branch from 658a0ef to a9ea215 Compare October 12, 2026 19:45
@aidandj

aidandj commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

Bumping this, tagging a couple recent contributors to see if anyone has feedback. Didn't see any instructions for requesting reviewers in the readme.

@stefanvanburen @ajeetdsouza @pkwarren

) error {
field := desc.Fields().Get(0)
p.setDescription(field, schema)
p.setDeprecated(field, schema)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed - the wrapper types underlying value won't be deprecated.

switch options := desc.Options().(type) {
case *descriptorpb.MessageOptions:
deprecated = options.GetDeprecated()
case *descriptorpb.FieldOptions:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs to also handle:

  • enum / enum value deprecation.
  • deprecation at the file level (this probably should be reflected in all messages/enum types in the file but not necessarily all fields of every message).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deprecation at the file level (this probably should be reflected in all messages/enum types in the file but not necessarily all fields of every message).

What is your reasoning for this? When I have implemented deprecation in the past I have just marked anything defined in the file as deprecated. A field is deprecated if the file is deprecated imo.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thinking was only to propagate it down to where it is needed. Since JSON schema has no concept of file level deprecation, we'd have to copy it down to messages/enums but not any further. This also matches the behavior of code generators which just mark the types deprecated and not all of the fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you expect for enum/enum value deprecation?

For enum deprecation, the enum is inlined into the field

        "enumValue": {
          "enum": [
            "ENUM_VAL1",
            "ENUM_VAL2",
            "ENUM_VAL7"
          ],
          "title": "Enum",
          "type": "string"
        },

So we could deprecate any field that referenced a deprecated enum

        "enumValue": {
          "enum": [
            "ENUM_VAL1",
            "ENUM_VAL2",
            "ENUM_VAL7"
          ],
          "title": "Enum",
          "type": "string",
          "deprecated": true
        },

For enum values there is no way to directly deprecate them. I found some suggestions to use an anyOf

"anyOf": [
  { "enum": ["red", "amber"] },
  { "enum": ["green"], "deprecated": true }
]

Thoughts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also matches the behavior of code generators which just mark the types deprecated and not all of the fields.

I checked the go code generator, and in this repo you can see the file deprecation deprecates fields

// Deprecated: The entire proto file buf/protoschema/test/v1/deprecated_file.proto is marked as deprecated.
func (x *DeprecatedFileTest) GetStringField() string {
	if x != nil {
		return x.StringField
	}
	return ""
}

mypy-protobuf (disclaimer, i wrote the deprecation generation code) deprecates fields when the file is deprecated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anyOf change pushed. Pushed file level deprecation for message enums, waiting on a decision on matching go/mypy-protobuf.

I had an LLM look at a few other languages for comparison

Language Marker Message Enum Field Enum value Service / RPC
Go // Deprecated: ✅ ✅ ✅ ✅ ✅
Python (mypy-protobuf) @typing.deprecated(...) ✅ ✅ ✅ ✅ —
TypeScript (protobuf-es) @deprecated ✅ ✅ ❌ ❌ ✅ / ❌
Objective-C GPB_DEPRECATED_MSG ✅ ✅ ❌ ❌ —
C++ [[deprecated]] ❌ ❌ ❌ ❌ ❌
Java @java.lang.Deprecated ❌ ❌ ❌ ❌ ❌
C# [ObsoleteAttribute] ❌ ❌ ❌ ❌ ❌
Kotlin @kotlin.Deprecated ❌ ❌ ❌ ❌ ❌
PHP @deprecated phpdoc ❌ ❌ ❌ ❌ ❌
Ruby none in .rb ❌ ❌ ❌ ❌ ❌

I clearly lean towards the go/mypy option. But can do whatever the maintainers want.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into that more. What is the behavior of JSON schema consumers (editors and other tools) when encountering a deprecated message if the field isn't marked deprecated as well? Do they warn based on field usage if the message is deprecated?

I'm fine to change the approach but it will make the generated schemas larger.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good thinking, I'll survey a couple of them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal use case is generating tooltip from json schema, in which case i would rather just check field deprecation before adding a strikethrough, instead of checking field and message deprecation

VSCode renders a tooltip on the whole message either way, no difference

Other tools either don't support deprecated, or pass it on.

I do think that for "correctness" a deprecated file means everything that can be deprecated inside it is deprecated. Otherwise you are always checking if a message is deprecated when checking if a field is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking if you had made a decision, or had time to review again.

A file-level `option deprecated = true` deprecates everything declared in
that file, so types declared there are marked even without their own
option. Fields and enum values inherit through their parent type instead.
Mark a field whose enum type is deprecated, and split deprecated enum
values into their own schemas: JSON Schema can only annotate a whole
schema, not individual members of an enum array, so each deprecated value
becomes its own `const` branch. Validation is unaffected -- every declared
value is still accepted.
@pkwarren
pkwarren merged commit 19b879c into bufbuild:main Oct 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants