microsoft / microsoft/typespec
[MRW Serialization] Emit JsonModelWriteCore and explicit interface members for abstract discriminated base models with known subtypes
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
## Description
When a model is an abstract discriminated base with known derived types (via `discriminatedSubtypes`) but does NOT extend a system type (like `ResourceData` or `TrackedResourceData`), `MrwSerializationTypeDefinition` does not emit:
- `JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options)` (protected virtual)
- `void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)` (explicit interface)
- `BinaryData IPersistableModel.Write(ModelReaderWriterOptions options)` (explicit interface)
- `T IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options)` (explicit interface)
- `string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options)` (explicit interface)
The model **does** emit:
- `PersistableModelCreateCore`
- `PersistableModelWriteCore`
- `JsonModelCreateCore`
- `Deserialize` (static)
## Impact
Derived types call `base.JsonModelWriteCore(writer, options)` to serialize base properties (discriminator, raw data). Without the base method, the generated code fails to compile.
The explicit interface implementations are needed so that the model satisfies the `IJsonModel` and `IPersistableModel` contracts required by the `ModelReaderWriter` framework.
## Expected Behavior
For abstract discriminated base models with known subtypes, `MrwSerializationTypeDefinition` should emit all serialization methods (both `*Core` methods and explicit interface implementations), regardless of whether the model extends a system type.
## Current Workaround
The Azure Management Generator (`InheritableSystemObjectModelVisitor`) defensively adds these methods via the visitor pattern if they are missing from MTG output. See: https://github.com/Azure/azure-sdk-for-net/issues/59437
## Minimum Reproduction
```typespec
import "@azure-tools/typespec-azure-resource-manager";
using Azure.ResourceManager;
@armProviderNamespace
@service(#{title: "TestService"})
@versioned(Versions)
@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5)
namespace TestService;
enum Versions {
v2024_01_01: "2024-01-01",
}
interface Operations extends Azure.ResourceManager.Operations {}
// A tracked resource that references a standalone polymorphic model
model TestResource is TrackedResource {
...ResourceNameParameter;
}
model TestResourceProperties {
identifier: ResourceIdentifier;
}
@armResourceOperations
interface TestResources {
get is ArmResourceRead;
createOrUpdate is ArmResourceCreateOrReplaceAsync;
}
// Standalone discriminated base - NOT extending any system type (ResourceData, etc.)
// Has known derived types via @discriminator
@discriminator("type")
model ResourceIdentifier {
type: string;
}
// Known derived type - this creates discriminatedSubtypes on the base
model AzureResourceIdentifier extends ResourceIdentifier {
type: "AzureResource";
azureResourceId: string;
}
```
### Steps to reproduce
1. Compile the above TypeSpec with the C# management emitter
2. Check the generated `ResourceIdentifier.Serialization.cs`
3. Observe that `JsonModelWriteCore`, `IJsonModel.Write`, `IPersistableModel.Write/Create/GetFormatFromOptions` are missing
4. The generated `UnknownResourceIdentifier.Serialization.cs` (or `AzureResourceIdentifier.Serialization.cs`) calls `base.JsonModelWriteCore(writer, options)` which fails to compile
### Key difference from working cases
- `LimitJsonObject` (discriminated base **without** known subtypes in TypeSpec) → works correctly, all methods emitted
- `PolyDevice` (discriminated base extending `DiscriminatedExtensionResource` system type) → works correctly via `InheritableSystemObjectModelVisitor`
- **This case**: discriminated base **with** known subtypes, **not** extending system type → missing methods
Contributor guide
Assessment
This issue has not been assessed yet.