From bc1c599517b1e945a7410d19410f5b867a227590 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Tue, 3 Sep 2024 16:22:21 +0800 Subject: [PATCH] Java sync to 16e2939c38dec8bb5322214d1a99f2c44284a2bf (#4293) I didn't include https://github.com/Azure/autorest.java/pull/2936 I will remove prenamer in next PR. --- .../emitter/src/code-model-builder.ts | 287 +- .../emitter/src/common/schemas/usage.ts | 4 +- .../core/extension/jsonrpc/Connection.java | 2 +- .../model/codemodel/SchemaContext.java | 5 - .../core/extension/plugin/JavaSettings.java | 48 +- .../ClientModelPropertiesManager.java | 11 + .../PolymorphicDiscriminatorHandler.java | 20 +- .../core/mapper/GraalVmConfigMapper.java | 3 +- .../core/model/clientmodel/ClientModel.java | 14 + .../clientmodel/ImplementationDetails.java | 9 - .../core/template/ModelTemplate.java | 37 +- .../StreamSerializationModelTemplate.java | 3968 ++++++++--------- .../src/main/resources/pom.xml | 5 - ...StreamStyleSerializationModelTemplate.java | 2 +- .../implementation/models/AbstractModel.java | 9 +- .../implementation/models/RealModel.java | 21 +- .../cadl/armresourceprovider/models/Dog.java | 3 +- .../armresourceprovider/models/Golden.java | 20 +- .../com/cadl/naming/models/BytesData.java | 20 +- .../java/com/cadl/naming/models/Data.java | 22 +- .../main/java/com/cadl/patch/models/Fish.java | 36 +- .../java/com/cadl/patch/models/Salmon.java | 30 +- .../java/com/cadl/patch/models/SawShark.java | 51 +- .../java/com/cadl/patch/models/Shark.java | 49 +- .../parameters/spread/AliasAsyncClient.java | 4 +- .../com/parameters/spread/AliasClient.java | 4 +- .../parameters/spread/ModelAsyncClient.java | 4 +- .../com/parameters/spread/ModelClient.java | 4 +- .../enumdiscriminator/models/Cobra.java | 21 +- .../enumdiscriminator/models/Dog.java | 9 +- .../enumdiscriminator/models/Golden.java | 21 +- .../enumdiscriminator/models/Snake.java | 9 +- .../enumnesteddiscriminator/models/Fish.java | 9 +- .../models/GoblinShark.java | 40 +- .../models/Salmon.java | 21 +- .../models/SawShark.java | 40 +- .../enumnesteddiscriminator/models/Shark.java | 30 +- .../nesteddiscriminator/models/Fish.java | 9 +- .../models/GoblinShark.java | 40 +- .../nesteddiscriminator/models/Salmon.java | 21 +- .../nesteddiscriminator/models/SawShark.java | 40 +- .../nesteddiscriminator/models/Shark.java | 30 +- .../singlediscriminator/models/Bird.java | 9 +- .../singlediscriminator/models/Dinosaur.java | 9 +- .../singlediscriminator/models/Eagle.java | 21 +- .../singlediscriminator/models/Goose.java | 21 +- .../singlediscriminator/models/SeaGull.java | 21 +- .../singlediscriminator/models/Sparrow.java | 21 +- .../singlediscriminator/models/TRex.java | 21 +- ...nownAdditionalPropertiesDiscriminated.java | 9 +- ...itionalPropertiesDiscriminatedDerived.java | 21 +- ...nownAdditionalPropertiesDiscriminated.java | 9 +- ...itionalPropertiesDiscriminatedDerived.java | 21 +- .../com/type/union/EnumsOnlyAsyncClient.java | 4 +- .../java/com/type/union/EnumsOnlyClient.java | 4 +- .../com/type/union/FloatsOnlyAsyncClient.java | 4 +- .../java/com/type/union/FloatsOnlyClient.java | 4 +- .../com/type/union/IntsOnlyAsyncClient.java | 4 +- .../java/com/type/union/IntsOnlyClient.java | 4 +- .../type/union/MixedLiteralsAsyncClient.java | 4 +- .../com/type/union/MixedLiteralsClient.java | 4 +- .../com/type/union/MixedTypesAsyncClient.java | 4 +- .../java/com/type/union/MixedTypesClient.java | 4 +- .../com/type/union/ModelsOnlyAsyncClient.java | 4 +- .../java/com/type/union/ModelsOnlyClient.java | 4 +- .../type/union/StringAndArrayAsyncClient.java | 4 +- .../com/type/union/StringAndArrayClient.java | 4 +- .../union/StringExtensibleAsyncClient.java | 4 +- .../type/union/StringExtensibleClient.java | 4 +- .../StringExtensibleNamedAsyncClient.java | 4 +- .../union/StringExtensibleNamedClient.java | 4 +- .../type/union/StringsOnlyAsyncClient.java | 4 +- .../com/type/union/StringsOnlyClient.java | 4 +- .../implementation/models/SendRequest.java | 14 +- .../implementation/models/SendRequest1.java | 14 +- .../implementation/models/SendRequest2.java | 14 +- .../implementation/models/SendRequest3.java | 14 +- .../implementation/models/SendRequest4.java | 14 +- .../implementation/models/SendRequest5.java | 14 +- .../implementation/models/SendRequest6.java | 14 +- .../implementation/models/SendRequest7.java | 14 +- .../implementation/models/SendRequest8.java | 14 +- .../implementation/models/SendRequest9.java | 14 +- .../parameters-spread_apiview_properties.json | 4 +- .../generator/http-client-generator/pom.xml | 47 +- .../typespec/http/client/generator/Main.java | 3 +- .../http/client/generator/TypeSpecPlugin.java | 2 + 87 files changed, 2951 insertions(+), 2540 deletions(-) diff --git a/packages/http-client-java/emitter/src/code-model-builder.ts b/packages/http-client-java/emitter/src/code-model-builder.ts index e3fbd83291..8e4c4ff870 100644 --- a/packages/http-client-java/emitter/src/code-model-builder.ts +++ b/packages/http-client-java/emitter/src/code-model-builder.ts @@ -67,7 +67,6 @@ import { SdkServiceMethod, SdkType, SdkUnionType, - UsageFlags, createSdkContext, getAllModels, getClientType, @@ -107,10 +106,7 @@ import { getHeaderFieldName, getPathParamName, getQueryParamName, - isBody, - isBodyRoot, isHeader, - isMultipartBodyProperty, isPathParam, isQueryParam, } from "@typespec/http"; @@ -463,6 +459,7 @@ export class CodeModelBuilder { schema instanceof ConstantSchema ) { const schemaUsage: SchemaContext[] | undefined = schema.usage; + // Public override Internal if (schemaUsage?.includes(SchemaContext.Public)) { const index = schemaUsage.indexOf(SchemaContext.Internal); @@ -471,11 +468,17 @@ export class CodeModelBuilder { } } - // Internal on Anonymous - if (schemaUsage?.includes(SchemaContext.Anonymous)) { - const index = schemaUsage.indexOf(SchemaContext.Internal); - if (index < 0) { - schemaUsage.push(SchemaContext.Internal); + // Internal on PublicSpread, but Public takes precedence + if (schemaUsage?.includes(SchemaContext.PublicSpread)) { + // remove PublicSpread as it now served its purpose + schemaUsage.splice(schemaUsage.indexOf(SchemaContext.PublicSpread), 1); + + // Public would override PublicSpread, hence do nothing if this schema is Public + if (!schemaUsage?.includes(SchemaContext.Public)) { + // set the model as Internal, so that it is not exposed to user + if (!schemaUsage.includes(SchemaContext.Internal)) { + schemaUsage.push(SchemaContext.Internal); + } } } } @@ -1350,6 +1353,12 @@ export class CodeModelBuilder { }); op.addParameter(parameter); + const jsonMergePatch = operationIsJsonMergePatch(sdkHttpOperation); + + const schemaIsPublicBeforeProcess = + schema instanceof ObjectSchema && + (schema as SchemaUsage).usage?.includes(SchemaContext.Public); + this.trackSchemaUsage(schema, { usage: [SchemaContext.Input] }); if (op.convenienceApi) { @@ -1359,130 +1368,143 @@ export class CodeModelBuilder { }); } - if (operationIsJsonMergePatch(sdkHttpOperation)) { + if (jsonMergePatch) { this.trackSchemaUsage(schema, { usage: [SchemaContext.JsonMergePatch] }); } if (op.convenienceApi && operationIsMultipart(sdkHttpOperation)) { this.trackSchemaUsage(schema, { serializationFormats: [KnownMediaType.Multipart] }); } - // Implicit body parameter would have usage flag: UsageFlags.Spread, for this case we need to do body parameter flatten - const bodyParameterFlatten = - sdkType.kind === "model" && sdkType.usage & UsageFlags.Spread && !this.isArm(); - - if (schema instanceof ObjectSchema && bodyParameterFlatten) { - // flatten body parameter - const parameters = sdkHttpOperation.parameters; - const bodyParameter = sdkHttpOperation.bodyParam; - // name the schema for documentation - schema.language.default.name = pascalCase(op.language.default.name) + "Request"; - - if (!parameter.language.default.name) { - // name the parameter for documentation - parameter.language.default.name = "request"; - } - - if (operationIsJsonMergePatch(sdkHttpOperation)) { - // skip model flatten, if "application/merge-patch+json" - schema.language.default.name = pascalCase(op.language.default.name) + "PatchRequest"; - return; - } - - this.trackSchemaUsage(schema, { usage: [SchemaContext.Anonymous] }); + if (op.convenienceApi) { + // Explicit body parameter @body or @bodyRoot would result to the existence of rawHttpOperation.parameters.body.property + // Implicit body parameter would result to rawHttpOperation.parameters.body.property be undefined + // see https://typespec.io/docs/libraries/http/cheat-sheet#data-types + const bodyParameterFlatten = + schema instanceof ObjectSchema && + sdkType.kind === "model" && + !rawHttpOperation.parameters.body?.property && + !this.isArm(); + + if (schema instanceof ObjectSchema && bodyParameterFlatten) { + // flatten body parameter + const parameters = sdkHttpOperation.parameters; + const bodyParameter = sdkHttpOperation.bodyParam; + + if (!parameter.language.default.name) { + // name the parameter for documentation + parameter.language.default.name = "request"; + } - if (op.convenienceApi && op.parameters) { - op.convenienceApi.requests = []; - const request = new Request({ - protocol: op.requests![0].protocol, - }); - request.parameters = []; - op.convenienceApi.requests.push(request); + if (jsonMergePatch) { + // skip model flatten, if "application/merge-patch+json" + if (sdkType.isGeneratedName) { + schema.language.default.name = pascalCase(op.language.default.name) + "PatchRequest"; + } + return; + } - // header/query/path params - for (const opParameter of parameters) { - this.addParameterOrBodyPropertyToCodeModelRequest( - opParameter, - op, - request, - schema, - parameter - ); + const schemaUsage = (schema as SchemaUsage).usage; + if (!schemaIsPublicBeforeProcess && schemaUsage?.includes(SchemaContext.Public)) { + // Public added in this op, change it to PublicSpread + // This means that if this op would originally add Public to this schema, it adds PublicSpread instead + schemaUsage?.splice(schemaUsage?.indexOf(SchemaContext.Public), 1); + this.trackSchemaUsage(schema, { usage: [SchemaContext.PublicSpread] }); } - // body param - if (bodyParameter) { - if (bodyParameter.type.kind === "model") { - for (const bodyProperty of bodyParameter.type.properties) { - if (bodyProperty.kind === "property") { - this.addParameterOrBodyPropertyToCodeModelRequest( - bodyProperty, - op, - request, - schema, - parameter - ); + + if (op.convenienceApi && op.parameters) { + op.convenienceApi.requests = []; + const request = new Request({ + protocol: op.requests![0].protocol, + }); + request.parameters = []; + op.convenienceApi.requests.push(request); + + // header/query/path params + for (const opParameter of parameters) { + this.addParameterOrBodyPropertyToCodeModelRequest( + opParameter, + op, + request, + schema, + parameter + ); + } + // body param + if (bodyParameter) { + if (bodyParameter.type.kind === "model") { + for (const bodyProperty of bodyParameter.type.properties) { + if (bodyProperty.kind === "property") { + this.addParameterOrBodyPropertyToCodeModelRequest( + bodyProperty, + op, + request, + schema, + parameter + ); + } } } } - } - request.signatureParameters = request.parameters; - - if (request.signatureParameters.length > 6) { - // create an option bag - const name = op.language.default.name + "Options"; - const namespace = getNamespace(rawHttpOperation.operation); - // option bag schema - const optionBagSchema = this.codeModel.schemas.add( - new GroupSchema(name, `Options for ${op.language.default.name} API`, { - language: { - default: { - namespace: namespace, - }, - java: { - namespace: this.getJavaNamespace(namespace), + request.signatureParameters = request.parameters; + + if (request.signatureParameters.length > 6) { + // create an option bag + const name = op.language.default.name + "Options"; + const namespace = getNamespace(rawHttpOperation.operation); + // option bag schema + const optionBagSchema = this.codeModel.schemas.add( + new GroupSchema(name, `Options for ${op.language.default.name} API`, { + language: { + default: { + namespace: namespace, + }, + java: { + namespace: this.getJavaNamespace(namespace), + }, }, - }, - }) - ); - request.parameters.forEach((it) => { - optionBagSchema.add( - new GroupProperty( - it.language.default.name, - it.language.default.description, - it.schema, - { - originalParameter: [it], - summary: it.summary, - required: it.required, - nullable: it.nullable, - readOnly: false, - serializedName: it.language.default.serializedName, - } - ) + }) ); - }); - - this.trackSchemaUsage(optionBagSchema, { usage: [SchemaContext.Input] }); - if (op.convenienceApi) { - this.trackSchemaUsage(optionBagSchema, { - usage: [op.internalApi ? SchemaContext.Internal : SchemaContext.Public], + request.parameters.forEach((it) => { + optionBagSchema.add( + new GroupProperty( + it.language.default.name, + it.language.default.description, + it.schema, + { + originalParameter: [it], + summary: it.summary, + required: it.required, + nullable: it.nullable, + readOnly: false, + serializedName: it.language.default.serializedName, + } + ) + ); }); - } - // option bag parameter - const optionBagParameter = new Parameter( - "options", - optionBagSchema.language.default.description, - optionBagSchema, - { - implementation: ImplementationLocation.Method, - required: true, - nullable: false, + this.trackSchemaUsage(optionBagSchema, { usage: [SchemaContext.Input] }); + if (op.convenienceApi) { + this.trackSchemaUsage(optionBagSchema, { + usage: [op.internalApi ? SchemaContext.Internal : SchemaContext.Public], + }); } - ); - request.signatureParameters = [optionBagParameter]; - request.parameters.forEach((it) => (it.groupedBy = optionBagParameter)); - request.parameters.push(optionBagParameter); + // option bag parameter + const optionBagParameter = new Parameter( + "options", + optionBagSchema.language.default.description, + optionBagSchema, + { + implementation: ImplementationLocation.Method, + required: true, + nullable: false, + } + ); + + request.signatureParameters = [optionBagParameter]; + request.parameters.forEach((it) => (it.groupedBy = optionBagParameter)); + request.parameters.push(optionBagParameter); + } } } } @@ -2340,24 +2362,6 @@ export class CodeModelBuilder { } } - private getParameterLocation(target: ModelProperty): ParameterLocation | "BodyProperty" { - if (isHeader(this.program, target)) { - return ParameterLocation.Header; - } else if (isQueryParam(this.program, target)) { - return ParameterLocation.Query; - } else if (isPathParam(this.program, target)) { - return ParameterLocation.Path; - } else if ( - isBody(this.program, target) || - isBodyRoot(this.program, target) || - isMultipartBodyProperty(this.program, target) - ) { - return ParameterLocation.Body; - } else { - return "BodyProperty"; - } - } - private isReadOnly(target: SdkModelPropertyType): boolean { const segment = target.__raw ? getSegment(this.program, target.__raw) !== undefined : false; if (segment) { @@ -2646,10 +2650,21 @@ export class CodeModelBuilder { }; // Exclude context that not to be propagated + const updatedSchemaUsage = (schema as SchemaUsage).usage?.filter( + (it) => it !== SchemaContext.Paged && it !== SchemaContext.PublicSpread + ); + const indexSpread = (schema as SchemaUsage).usage?.indexOf(SchemaContext.PublicSpread); + if ( + updatedSchemaUsage && + indexSpread && + indexSpread >= 0 && + !(schema as SchemaUsage).usage?.includes(SchemaContext.Public) + ) { + // Propagate Public, if schema is PublicSpread + updatedSchemaUsage.push(SchemaContext.Public); + } const schemaUsage = { - usage: (schema as SchemaUsage).usage?.filter( - (it) => it !== SchemaContext.Paged && it !== SchemaContext.Anonymous - ), + usage: updatedSchemaUsage, serializationFormats: (schema as SchemaUsage).serializationFormats?.filter( (it) => it !== KnownMediaType.Multipart ), diff --git a/packages/http-client-java/emitter/src/common/schemas/usage.ts b/packages/http-client-java/emitter/src/common/schemas/usage.ts index 087546f6c0..59e69d2c8e 100644 --- a/packages/http-client-java/emitter/src/common/schemas/usage.ts +++ b/packages/http-client-java/emitter/src/common/schemas/usage.ts @@ -19,8 +19,8 @@ export enum SchemaContext { /** Schema is used from the pageable operation. This usage does not propagate. */ Paged = "paged", - /** Schema as anonymous model. This usage does not propagate. */ - Anonymous = "anonymous", + /** Schema as spread/flatten model. "Public", if present in usage, will have precedence over "PublicSpread". This usage does not propagate. Instead, it propagate "Public". */ + PublicSpread = "spread", /** Schema is used in json-merge-patch operation */ JsonMergePatch = "json-merge-patch", diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/jsonrpc/Connection.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/jsonrpc/Connection.java index 13e019a6c3..adae516953 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/jsonrpc/Connection.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/jsonrpc/Connection.java @@ -52,7 +52,7 @@ public Connection(OutputStream writer, InputStream input) { this.requestId = new AtomicInteger(0); } - private boolean isAlive = true; + private volatile boolean isAlive = true; /** * Stops the connection. diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/model/codemodel/SchemaContext.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/model/codemodel/SchemaContext.java index b1efd5b9ce..d0d5181aa8 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/model/codemodel/SchemaContext.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/model/codemodel/SchemaContext.java @@ -35,11 +35,6 @@ public enum SchemaContext { */ PAGED("paged"), - /** - * The schema is used as an anonymous type. - */ - ANONYMOUS("anonymous"), - /** * The schema is used internally. */ diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java index 25fa19a5cc..70418dc02c 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java @@ -180,7 +180,9 @@ public static JavaSettings getInstance() { getBooleanValue(host, "null-byte-array-maps-to-empty-array", false), getBooleanValue(host, "graal-vm-config", false), getStringValue(host, "flavor", "Azure"), - getBooleanValue(host, "disable-typed-headers-methods", false) + getBooleanValue(host, "disable-typed-headers-methods", false), + getBooleanValue(host, "share-jsonserializable-code", false), + getBooleanValue(host, "android", false) ); } return instance; @@ -284,6 +286,9 @@ private static Map parseStatusCodeMapping(JsonReader jsonReader * @param flavor The brand name we use to generate SDK. * @param disableTypedHeadersMethods Prevents generating REST API methods that include typed headers. If set to * true, {@code noCustomHeaders} will be ignored as no REST APIs with typed headers will be generated. + * @param shareJsonSerializableCode Whether models implementing {@code JsonSerializable} can attempt to share code + * for {@code toJson} and {@code fromJson}. + * @param android Whether to generate the Android client. */ private JavaSettings(AutorestSettings autorestSettings, Map modelerSettings, @@ -346,7 +351,9 @@ private JavaSettings(AutorestSettings autorestSettings, boolean nullByteArrayMapsToEmptyArray, boolean generateGraalVmConfig, String flavor, - boolean disableTypedHeadersMethods) { + boolean disableTypedHeadersMethods, + boolean shareJsonSerializableCode, + boolean android) { this.autorestSettings = autorestSettings; this.modelerSettings = new ModelerSettings(modelerSettings); @@ -445,6 +452,8 @@ private JavaSettings(AutorestSettings autorestSettings, this.generateGraalVmConfig = generateGraalVmConfig; this.flavor = flavor; this.disableTypedHeadersMethods = disableTypedHeadersMethods; + this.shareJsonSerializableCode = shareJsonSerializableCode; + this.android = android; } /** @@ -780,7 +789,7 @@ public final boolean isRegeneratePom() { public final String getFileHeaderText() { return fileHeaderText; } - + private final String serviceName; /** @@ -1643,6 +1652,39 @@ public boolean isDisableTypedHeadersMethods() { return disableTypedHeadersMethods; } + private final boolean shareJsonSerializableCode; + + /** + * Whether code generation will attempt to share {@code toJson} and {@code fromJson} code generated for + * {@code JsonSerializable} implementing models. + *

+ * When this feature is enabled, the code generated for {@code toJson} and {@code fromJson} will attempt to be + * shared across polymorphic types. This will only happen when all polymorphic models in the polymorphic hierarchy + * are in the same package and meet a few criteria, such as no constructor arguments in the {@code fromJson} case. + * The aim of this feature is to reduce the amount of code generated by {@code JsonSerializable}, which will aid in + * testing all cases and should help the JVM determine more quickly when to JIT certain code. + *

+ * This feature is still experimental, therefore is disabled by default. In the future this feature will become + * enabled default, and then further out this feature will always be enabled without the ability to be configured. + * + * @return Whether code generation will attempt to share {@code toJson} and {@code fromJson} code generated for + * {@code JsonSerializable} implementing models. + */ + public boolean isShareJsonSerializableCode() { + return shareJsonSerializableCode; + } + + private final boolean android; + + /** + * Whether to generate code for Android. + * + * @return Whether to generate code for Android. + */ + public boolean isAndroid() { + return android; + } + private static final String DEFAULT_CODE_GENERATION_HEADER = String.join("\n", "Code generated by Microsoft (R) AutoRest Code Generator %s", "Changes may cause incorrect behavior and will be lost if the code is regenerated."); diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/ClientModelPropertiesManager.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/ClientModelPropertiesManager.java index bfc1938030..e5792bc641 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/ClientModelPropertiesManager.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/ClientModelPropertiesManager.java @@ -39,6 +39,7 @@ */ public final class ClientModelPropertiesManager { private final ClientModel model; + private final JavaSettings settings; private final String deserializedModelName; private final boolean hasRequiredProperties; @@ -88,6 +89,7 @@ public ClientModelPropertiesManager(ClientModel model, JavaSettings settings) { Set possibleXmlNameVariableNames = new LinkedHashSet<>(Arrays.asList( "elementName", "xmlElementName", "deserializationElementName")); this.model = model; + this.settings = settings; this.deserializedModelName = "deserialized" + model.getName(); this.expectedDiscriminator = model.getSerializedName(); @@ -293,6 +295,15 @@ public ClientModel getModel() { return model; } + /** + * The {@link JavaSettings} being used to determine code generation. + * + * @return The {@link JavaSettings} being used to determine code generation. + */ + public JavaSettings getSettings() { + return settings; + } + /** * Gets the name of the variable used when deserializing an instance of the {@link #getModel() model}. * diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/PolymorphicDiscriminatorHandler.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/PolymorphicDiscriminatorHandler.java index d2984edd21..31d68e6445 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/PolymorphicDiscriminatorHandler.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/implementation/PolymorphicDiscriminatorHandler.java @@ -66,8 +66,8 @@ public static void declareField(ClientModel model, JavaClass classBlock, Consume } declareFieldInternal(model.getPolymorphicDiscriminator(), model, - ClientModelUtil.modelDefinesProperty(model, model.getPolymorphicDiscriminator()), classBlock, - addGeneratedAnnotation, addFieldAnnotations, settings); + model.isPolymorphicDiscriminatorDefinedByModel(), classBlock, addGeneratedAnnotation, addFieldAnnotations, + settings); } private static void declareFieldInternal(ClientModelProperty discriminator, ClientModel model, @@ -86,14 +86,14 @@ private static void declareFieldInternal(ClientModelProperty discriminator, Clie // There can be cases with polymorphic discriminators where they have both a default value and are // required, in which case the default value will be set in the constructor. boolean discriminatorFieldIsInitialized = (!discriminatorUsedInConstructor || discriminator.isConstant()) - && (discriminatorValue != null && !allPolymorphicModelsInSamePackage); + && (discriminatorValue != null && (!allPolymorphicModelsInSamePackage || !settings.isShareJsonSerializableCode())); String fieldSignature = discriminatorFieldIsInitialized ? propertyType + " " + propertyName + " = " + discriminatorValue : propertyType + " " + propertyName; boolean generateCommentAndAnnotations = discriminatorUsedInConstructor - || (allPolymorphicModelsInSamePackage && discriminatorDefinedByModel) - || !allPolymorphicModelsInSamePackage; + || (allPolymorphicModelsInSamePackage && discriminatorDefinedByModel && settings.isShareJsonSerializableCode()) + || (!allPolymorphicModelsInSamePackage || !settings.isShareJsonSerializableCode()); if (generateCommentAndAnnotations) { classBlock.blockComment(comment -> comment.line(discriminator.getDescription())); @@ -106,9 +106,9 @@ private static void declareFieldInternal(ClientModelProperty discriminator, Clie } else { // If the model defines the discriminator and all models in the polymorphic hierarchy are in the same // package, make it package-private to allow derived models to access it. - if (allPolymorphicModelsInSamePackage && discriminatorDefinedByModel) { + if (allPolymorphicModelsInSamePackage && discriminatorDefinedByModel && settings.isShareJsonSerializableCode()) { classBlock.memberVariable(JavaVisibility.PackagePrivate, fieldSignature); - } else if (!allPolymorphicModelsInSamePackage) { + } else if (!allPolymorphicModelsInSamePackage || !settings.isShareJsonSerializableCode()) { classBlock.privateMemberVariable(fieldSignature); } } @@ -121,7 +121,7 @@ public static void initializeInConstructor(ClientModel model, JavaBlock construc // Polymorphic models are contained in different packages, so the discriminator value was set in the field // declaration. - if (!model.isAllPolymorphicModelsInSamePackage()) { + if (!model.isAllPolymorphicModelsInSamePackage() || !settings.isShareJsonSerializableCode()) { return; } @@ -150,10 +150,10 @@ private static void initializeInConstructorInternal(ClientModelProperty discrimi * * @return Whether a getter method should be generated for the discriminator property. */ - public static boolean generateGetter(ClientModel model, ClientModelProperty discriminator) { + public static boolean generateGetter(ClientModel model, ClientModelProperty discriminator, JavaSettings settings) { // If all the polymorphic models aren't in the same package the getter for the discriminator value will be // generated for each model as each model defines properties for all discriminators. - if (!model.isAllPolymorphicModelsInSamePackage()) { + if (!model.isAllPolymorphicModelsInSamePackage() || !settings.isShareJsonSerializableCode()) { return true; } diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/mapper/GraalVmConfigMapper.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/mapper/GraalVmConfigMapper.java index a416737c29..79c09374cd 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/mapper/GraalVmConfigMapper.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/mapper/GraalVmConfigMapper.java @@ -8,6 +8,7 @@ import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClientModel; import com.microsoft.typespec.http.client.generator.core.model.clientmodel.EnumType; import com.microsoft.typespec.http.client.generator.core.model.clientmodel.GraalVmConfig; +import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ImplementationDetails; import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ServiceClient; import java.util.Collection; @@ -56,7 +57,7 @@ public GraalVmConfig map(ServiceAndModel data) { .map(e -> e.getPackage() + "." + e.getName()) .collect(Collectors.toList()); reflects.addAll(data.models.stream() - .filter(m -> !streamStyle || (m.getImplementationDetails() != null && m.getImplementationDetails().isException())) + .filter(m -> !streamStyle || m.hasUsage(ImplementationDetails.Usage.EXCEPTION)) .map(m -> m.getPackage() + "." + m.getName()) .collect(Collectors.toList())); reflects.addAll(data.enums.stream() diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ClientModel.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ClientModel.java index 2b19bfb269..ea29091855 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ClientModel.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ClientModel.java @@ -418,6 +418,20 @@ public ImplementationDetails getImplementationDetails() { return implementationDetails; } + /** + * Checks whether the model has the given {@link ImplementationDetails.Usage}. + *

+ * If {@link #getImplementationDetails()} or {@link ImplementationDetails#getUsages()} is null this will always + * return false. + * + * @return Whether the model has the given {@link ImplementationDetails.Usage}. + */ + public boolean hasUsage(ImplementationDetails.Usage usage) { + return implementationDetails != null + && implementationDetails.getUsages() != null + && implementationDetails.getUsages().contains(usage); + } + /** * List the properties that have access (getter or setter) methods. *

diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ImplementationDetails.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ImplementationDetails.java index 30a4542e85..65772db7cf 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ImplementationDetails.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/clientmodel/ImplementationDetails.java @@ -49,13 +49,6 @@ public enum Usage { */ PAGED("paged"), - /** - * Anonymous model. - *

- * Codegen may choose to not generate class for it, or generate class in implementation package. - */ - ANONYMOUS("anonymous"), - /** * External model. *

@@ -144,8 +137,6 @@ public static Usage fromSchemaContext(SchemaContext schemaContext) { return PUBLIC; case PAGED: return PAGED; - case ANONYMOUS: - return ANONYMOUS; case INTERNAL: return INTERNAL; case JSON_MERGE_PATCH: diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTemplate.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTemplate.java index 7435645901..d97354e335 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTemplate.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTemplate.java @@ -4,6 +4,7 @@ package com.microsoft.typespec.http.client.generator.core.template; import com.microsoft.typespec.http.client.generator.core.extension.plugin.JavaSettings; +import com.microsoft.typespec.http.client.generator.core.implementation.ClientModelPropertiesManager; import com.microsoft.typespec.http.client.generator.core.implementation.PolymorphicDiscriminatorHandler; import com.microsoft.typespec.http.client.generator.core.model.clientmodel.Annotation; import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ArrayType; @@ -76,6 +77,7 @@ public final void write(ClientModel model, JavaFile javaFile) { final boolean requireSerialization = modelRequireSerialization(model); JavaSettings settings = JavaSettings.getInstance(); + ClientModelPropertiesManager propertiesManager = new ClientModelPropertiesManager(model, settings); Set imports = settings.isStreamStyleSerialization() ? new StreamStyleImports() : new HashSet<>(); addImports(imports, model, settings); @@ -146,7 +148,7 @@ public final void write(ClientModel model, JavaFile javaFile) { discriminator -> addFieldAnnotations(model, discriminator, classBlock, settings), settings); // properties - addProperties(model, classBlock, settings); + addProperties(propertiesManager, classBlock); // add jsonMergePatch related properties and accessors if (ClientModelUtil.isJsonMergePatchModel(model, settings)) { @@ -159,7 +161,7 @@ public final void write(ClientModel model, JavaFile javaFile) { : JavaVisibility.Public; addModelConstructor(model, modelConstructorVisibility, settings, classBlock); - for (ClientModelProperty property : getFieldProperties(model, settings)) { + for (ClientModelProperty property : getFieldProperties(propertiesManager)) { final boolean propertyIsReadOnly = immutableModel || property.isReadOnly(); IType propertyWireType = property.getWireType(); @@ -170,7 +172,7 @@ public final void write(ClientModel model, JavaFile javaFile) { : JavaVisibility.Public; if (!property.isPolymorphicDiscriminator() - || PolymorphicDiscriminatorHandler.generateGetter(model, property)) { + || PolymorphicDiscriminatorHandler.generateGetter(model, property, settings)) { generateGetterJavadoc(classBlock, property); addGeneratedAnnotation(classBlock); if (property.isAdditionalProperties() && !settings.isStreamStyleSerialization()) { @@ -323,7 +325,7 @@ public final void write(ClientModel model, JavaFile javaFile) { } if (requireSerialization) { - writeStreamStyleSerialization(classBlock, model, settings); + writeStreamStyleSerialization(classBlock, propertiesManager); } }); } @@ -334,7 +336,9 @@ private static boolean addOverrideAnnotationToGetter(JavaVisibility visibility, return false; } - if (property.isPolymorphicDiscriminator() && model.isAllPolymorphicModelsInSamePackage()) { + if (settings.isShareJsonSerializableCode() + && property.isPolymorphicDiscriminator() + && model.isAllPolymorphicModelsInSamePackage()) { return false; } @@ -525,13 +529,11 @@ protected void addXmlNamespaceConstants(ClientModel model, JavaClass classBlock) /** * Adds the property fields to a class. * - * @param model The client model. * @param classBlock The Java class. - * @param settings AutoRest configuration settings. */ - private void addProperties(ClientModel model, JavaClass classBlock, JavaSettings settings) { - for (ClientModelProperty property : getFieldProperties(model, settings)) { - addProperty(property, model, classBlock, settings); + private void addProperties(ClientModelPropertiesManager propertiesManager, JavaClass classBlock) { + for (ClientModelProperty property : getFieldProperties(propertiesManager)) { + addProperty(property, propertiesManager.getModel(), classBlock, propertiesManager.getSettings()); } } @@ -609,14 +611,12 @@ private void addProperty(ClientModelProperty property, ClientModel model, JavaCl /** * Get properties to generate as fields of the class. - * @param model the model to generate class of - * @param settings JavaSettings + * @param propertiesManager The properties manager. * @return properties to generate as fields of the class */ - protected List getFieldProperties(ClientModel model, JavaSettings settings) { - return Stream.concat( - model.getParentPolymorphicDiscriminators().stream(), - model.getProperties().stream() + protected List getFieldProperties(ClientModelPropertiesManager propertiesManager) { + ClientModel model = propertiesManager.getModel(); + return Stream.concat(model.getParentPolymorphicDiscriminators().stream(), model.getProperties().stream() ).collect(Collectors.toList()); } @@ -1219,10 +1219,9 @@ private static boolean modelRequireSerialization(ClientModel model) { * the model uses. * * @param classBlock The class block where serialization methods will be written. - * @param model The model. - * @param settings Autorest generation settings. + * @param propertiesManager The properties manager. */ - protected void writeStreamStyleSerialization(JavaClass classBlock, ClientModel model, JavaSettings settings) { + protected void writeStreamStyleSerialization(JavaClass classBlock, ClientModelPropertiesManager propertiesManager) { // No-op, meant for StreamSerializationModelTemplate. } diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/StreamSerializationModelTemplate.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/StreamSerializationModelTemplate.java index 00957dd21a..83ad1ba821 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/StreamSerializationModelTemplate.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/StreamSerializationModelTemplate.java @@ -45,6 +45,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -143,121 +144,130 @@ static void xmlWrapperClassXmlSerializableImplementation(JavaClass classBlock, S addGeneratedAnnotation.accept(classBlock); classBlock.annotation("Override"); - classBlock.publicMethod("XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException", writerMethod -> { - writerMethod.line("rootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? \"" + xmlRootElementName + "\" : rootElementName;"); - String writeStartElement = (xmlRootElementNamespace != null) - ? "xmlWriter.writeStartElement(\"" + xmlRootElementNamespace + "\", rootElementName);" - : "xmlWriter.writeStartElement(rootElementName);"; - writerMethod.line(writeStartElement); - - writerMethod.ifBlock(xmlElementNameCamelCase + " != null", ifAction -> { - String xmlWrite = elementType.xmlSerializationMethodCall("xmlWriter", xmlListElementName, - xmlListElementNamespace, "element", false, false, false); - ifAction.line("for (%s element : %s) {", elementType, xmlElementNameCamelCase); - ifAction.indent(() -> ifAction.line(xmlWrite + ";")); - ifAction.line("}"); - }); + classBlock.publicMethod( + "XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException", writerMethod -> { + writerMethod.line("rootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? \"" + xmlRootElementName + + "\" : rootElementName;"); + String writeStartElement = (xmlRootElementNamespace != null) + ? "xmlWriter.writeStartElement(\"" + xmlRootElementNamespace + "\", rootElementName);" + : "xmlWriter.writeStartElement(rootElementName);"; + writerMethod.line(writeStartElement); + + writerMethod.ifBlock(xmlElementNameCamelCase + " != null", ifAction -> { + String xmlWrite = elementType.xmlSerializationMethodCall("xmlWriter", xmlListElementName, + xmlListElementNamespace, "element", false, false, false); + ifAction.line("for (%s element : %s) {", elementType, xmlElementNameCamelCase); + ifAction.indent(() -> ifAction.line(xmlWrite + ";")); + ifAction.line("}"); + }); - writerMethod.methodReturn("xmlWriter.writeEndElement()"); - }); + writerMethod.methodReturn("xmlWriter.writeEndElement()"); + }); addGeneratedAnnotation.accept(classBlock); classBlock.publicStaticMethod(wrapperClassName + " fromXml(XmlReader xmlReader) throws XMLStreamException", readerMethod -> readerMethod.methodReturn("fromXml(xmlReader, null)")); addGeneratedAnnotation.accept(classBlock); - classBlock.publicStaticMethod(wrapperClassName + " fromXml(XmlReader xmlReader, String rootElementName) throws XMLStreamException", readerMethod -> { - readerMethod.line("rootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? \"" + xmlRootElementName + "\" : rootElementName;"); - String readObject = (xmlRootElementNamespace != null) - ? "return xmlReader.readObject(\"" + xmlRootElementNamespace + "\", rootElementName, reader -> {" - : "return xmlReader.readObject(rootElementName, reader -> {"; - - readerMethod.line(readObject); - readerMethod.indent(() -> { - readerMethod.line(iterableType + " items = null;"); - readerMethod.line(); - readerMethod.line("while (reader.nextElement() != XmlToken.END_ELEMENT) {"); + classBlock.publicStaticMethod( + wrapperClassName + " fromXml(XmlReader xmlReader, String rootElementName) throws XMLStreamException", + readerMethod -> { + readerMethod.line("rootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? \"" + xmlRootElementName + + "\" : rootElementName;"); + String readObject = (xmlRootElementNamespace != null) + ? "return xmlReader.readObject(\"" + xmlRootElementNamespace + "\", rootElementName, reader -> {" + : "return xmlReader.readObject(rootElementName, reader -> {"; + + readerMethod.line(readObject); readerMethod.indent(() -> { - readerMethod.line("QName elementName = reader.getElementName();"); - String condition = getXmlNameConditional(xmlListElementName, xmlListElementNamespace, "elementName", false); + readerMethod.line(iterableType + " items = null;"); readerMethod.line(); - readerMethod.ifBlock(condition, ifBlock -> { - ifBlock.ifBlock("items == null", ifBlock2 -> ifBlock2.line("items = new ArrayList<>();")); - ifBlock.line(); - - // TODO (alzimmer): Insert XML object reading logic. - ifBlock.line("items.add(" + getSimpleXmlDeserialization(elementType, "reader", null, null, - null, false) + ");"); - }).elseBlock(elseBlock -> elseBlock.line("reader.nextElement();")); - }); - readerMethod.line("}"); + readerMethod.line("while (reader.nextElement() != XmlToken.END_ELEMENT) {"); + readerMethod.indent(() -> { + readerMethod.line("QName elementName = reader.getElementName();"); + String condition = getXmlNameConditional(xmlListElementName, xmlListElementNamespace, + "elementName", false); + readerMethod.line(); + readerMethod.ifBlock(condition, ifBlock -> { + ifBlock.ifBlock("items == null", ifBlock2 -> ifBlock2.line("items = new ArrayList<>();")); + ifBlock.line(); + + // TODO (alzimmer): Insert XML object reading logic. + ifBlock.line( + "items.add(" + getSimpleXmlDeserialization(elementType, null, null, null, + false) + ");"); + }).elseBlock(elseBlock -> elseBlock.line("reader.nextElement();")); + }); + readerMethod.line("}"); - readerMethod.methodReturn("new " + wrapperClassName + "(items)"); + readerMethod.methodReturn("new " + wrapperClassName + "(items)"); + }); + readerMethod.line("});"); }); - readerMethod.line("});"); - }); } - @Override - protected void addFieldAnnotations(ClientModel model, ClientModelProperty property, JavaClass classBlock, JavaSettings settings) { - // no-op as stream-style serialization doesn't add any field-level annotations. - } - - @Override - protected void writeStreamStyleSerialization(JavaClass classBlock, ClientModel model, JavaSettings settings) { - // Early out as strongly-typed headers do their own thing. - if (model.isStronglyTypedHeader()) { - return; + private static String getXmlNameConditional(String localPart, String namespace, String elementName, + boolean namespaceIsConstant) { + String condition = "\"" + localPart + "\".equals(" + elementName + ".getLocalPart())"; + if (!CoreUtils.isNullOrEmpty(namespace)) { + if (namespaceIsConstant) { + condition += " && " + namespace + ".equals(" + elementName + ".getNamespaceURI())"; + } else { + condition += " && \"" + namespace + "\".equals(" + elementName + ".getNamespaceURI())"; + } } - ClientModelPropertiesManager propertiesManager = new ClientModelPropertiesManager(model, settings); + return condition; + } - if (model.getXmlName() != null) { - writeToXml(classBlock, propertiesManager, Templates.getModelTemplate()::addGeneratedAnnotation); - writeFromXml(classBlock, model, propertiesManager, settings, - Templates.getModelTemplate()::addGeneratedAnnotation); + private static String getSimpleXmlDeserialization(IType wireType, String elementName, String attributeName, + String attributeNamespace, boolean namespaceIsConstant) { + if (wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { + return CoreUtils.isNullOrEmpty(elementName) + ? wireType + ".fromXml(reader)" + : wireType + ".fromXml(reader, \"" + elementName + "\")"; } else { - if (ClientModelUtil.isJsonMergePatchModel(model, settings)) { - writeToJson(classBlock, propertiesManager, true, Templates.getModelTemplate()::addGeneratedAnnotation); - writeToJsonMergePatch(classBlock, propertiesManager, - Templates.getModelTemplate()::addGeneratedAnnotation); - } else { - writeToJson(classBlock, propertiesManager, false, Templates.getModelTemplate()::addGeneratedAnnotation); - } - writeFromJson(classBlock, model, propertiesManager, settings, - Templates.getModelTemplate()::addGeneratedAnnotation); - if (isManagementErrorSubclass(model, settings)) { - writeManagementErrorDeserializationMethod(classBlock, propertiesManager, settings, - Templates.getModelTemplate()::addGeneratedAnnotation); - } + return wireType.xmlDeserializationMethod("reader", attributeName, attributeNamespace, + namespaceIsConstant); } } + @Override + protected void addFieldAnnotations(ClientModel model, ClientModelProperty property, JavaClass classBlock, + JavaSettings settings) { + // no-op as stream-style serialization doesn't add any field-level annotations. + } + /** - * For stream-style-serialization, we generate shadow properties for read-only properties that's not in constructor. - * @param model the model to generate class of - * @param settings JavaSettings + * For stream-style-serialization, we generate shadow properties for read-only properties that's not in + * constructor. + * + * @param propertiesManager the properties manager * @return properties to generate as fields of the class */ @Override - protected List getFieldProperties(ClientModel model, JavaSettings settings) { - List fieldProperties = super.getFieldProperties(model, settings); + protected List getFieldProperties(ClientModelPropertiesManager propertiesManager) { + List fieldProperties = super.getFieldProperties(propertiesManager); // If the model is polymorphic and all the models in the polymorphic hierarchy are in the same package we don't // need to shade parent properties. - if (model.isPolymorphic() && model.isAllPolymorphicModelsInSamePackage()) { + if (canUseFromJsonShared(propertiesManager)) { return fieldProperties; } - Set propertySerializedNames = fieldProperties.stream().map(ClientModelProperty::getSerializedName) + Set propertySerializedNames = fieldProperties.stream() + .map(ClientModelProperty::getSerializedName) .collect(Collectors.toSet()); + ClientModel model = propertiesManager.getModel(); for (ClientModelProperty parentProperty : ClientModelUtil.getParentProperties(model, false)) { if (propertySerializedNames.contains(parentProperty.getSerializedName())) { continue; } propertySerializedNames.add(parentProperty.getSerializedName()); - if (!parentProperty.isPolymorphicDiscriminator() // parent discriminators are already passed to children, see @see in method javadoc - && ClientModelUtil.readOnlyNotInCtor(model, parentProperty, settings) // we shadow parent read-only properties in child class + if (!parentProperty.isPolymorphicDiscriminator() + // parent discriminators are already passed to children, see @see in method javadoc + && ClientModelUtil.readOnlyNotInCtor(model, parentProperty, propertiesManager.getSettings()) + // we shadow parent read-only properties in child class || parentProperty.getClientFlatten()) { // we shadow parent flattened property in child class fieldProperties.add(parentProperty); } @@ -265,12 +275,21 @@ protected List getFieldProperties(ClientModel model, JavaSe return fieldProperties; } + private static boolean canUseFromJsonShared(ClientModelPropertiesManager propertiesManager) { + // If the model is part of a polymorphic hierarchy and all models in the polymorphic hierarchy are in the same + // package we can generate a package-private 'toJsonShared' method that can handle deserializing properties + // defined in the parent class(es). + // This will prevent duplicating the deserialization logic for parent properties in each subclass. + return propertiesManager.getSettings().isShareJsonSerializableCode() + && !propertiesManager.hasConstructorArguments() + && propertiesManager.getModel().isAllPolymorphicModelsInSamePackage(); + } + /** - * Get the property reference referring to the local(field) flattened property. - * Additionally, in Stream-Style, parent property reference count as well. Since in Stream-Style, the flattened model property - * will be shadowed in child class. - * For example, for the property1FromParent collected by {@link #getClientModelPropertyReferences(ClientModel)} on model2, - * it looks like: + * Get the property reference referring to the local(field) flattened property. Additionally, in Stream-Style, + * parent property reference count as well. Since in Stream-Style, the flattened model property will be shadowed in + * child class. For example, for the property1FromParent collected by + * {@link #getClientModelPropertyReferences(ClientModel)} on model2, it looks like: *

{@code
    *         FlattenedProperties
    *          - property1                    <--------------
@@ -287,20 +306,22 @@ protected List getFieldProperties(ClientModel model, JavaSe
    *              - targetProperty -> null
    * }
    * 
- * If called on property1FromParent collected from Model2, property1FromFlatten will be returned. - * If this method is called on property1FromFlatten collected from Model1, itself will be returned. + * If called on property1FromParent collected from Model2, property1FromFlatten will be returned. If this method is + * called on property1FromFlatten collected from Model1, itself will be returned. * * @param propertyReference propertyReference collected by {@link #getClientModelPropertyReferences(ClientModel)} - * @return the property reference referring to the local(field) flattened property, or parent flattening property reference, - * null if neither + * @return the property reference referring to the local(field) flattened property, or parent flattening property + * reference, null if neither */ @Override - protected ClientModelPropertyReference getLocalFlattenedModelPropertyReference(ClientModelPropertyReference propertyReference) { + protected ClientModelPropertyReference getLocalFlattenedModelPropertyReference( + ClientModelPropertyReference propertyReference) { if (propertyReference.isFromFlattenedProperty()) { return propertyReference; } else if (propertyReference.isFromParentModel()) { ClientModelPropertyAccess parentProperty = propertyReference.getReferenceProperty(); // parent property - if (parentProperty instanceof ClientModelPropertyReference && ((ClientModelPropertyReference) parentProperty).isFromFlattenedProperty()) { + if (parentProperty instanceof ClientModelPropertyReference + && ((ClientModelPropertyReference) parentProperty).isFromFlattenedProperty()) { return (ClientModelPropertyReference) parentProperty; } } @@ -309,14 +330,14 @@ protected ClientModelPropertyReference getLocalFlattenedModelPropertyReference(C } @Override - protected List getSuperSetters(ClientModel model, JavaSettings settings, List propertyReferences) { + protected List getSuperSetters(ClientModel model, JavaSettings settings, + List propertyReferences) { return super.getSuperSetters(model, settings, propertyReferences) .stream() // If the propertyReference is flattening property, then in Stream-Style we generate local getter/setter // for it, thus we don't need to generate super setter. - .filter(propertyReference -> - !((propertyReference instanceof ClientModelPropertyReference) - && ((ClientModelPropertyReference) propertyReference).isFromFlattenedProperty())) + .filter(propertyReference -> !((propertyReference instanceof ClientModelPropertyReference) + && ((ClientModelPropertyReference) propertyReference).isFromFlattenedProperty())) .collect(Collectors.toList()); } @@ -334,1276 +355,1133 @@ protected List getValidationProperties(ClientModel model) { } /** - * write toJson() method. - *

- * If it is a JsonMergePatch model, toJson() should first check jsonMergePatch flag value and then do serialization - * accordingly. + * Whether the given model is subclass of ManagementError, which needs special deserialization adaption. * - * @param classBlock The class block to write the toJson method to. - * @param propertiesManager The properties manager for the model. - * @param isJsonMergePatch Whether the serialization is for a JSON merge patch. - * @param addGeneratedAnnotation The consumer to add the generated annotation to the class block. + * @param model the model to check + * @return whether the given model is subclass of ManagementError */ - private static void writeToJson(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - boolean isJsonMergePatch, Consumer addGeneratedAnnotation) { - boolean callToJsonSharedForParentProperties = !isJsonMergePatch - && propertiesManager.getModel().isAllPolymorphicModelsInSamePackage() - && !CoreUtils.isNullOrEmpty(propertiesManager.getModel().getParentModelName()); - boolean callToJsonSharedForThisProperties = !isJsonMergePatch - && propertiesManager.getModel().isAllPolymorphicModelsInSamePackage() - && propertiesManager.getModel().isPolymorphicParent(); - - classBlock.javadocComment(JavaJavadocComment::inheritDoc); - addGeneratedAnnotation.accept(classBlock); - classBlock.annotation("Override"); - classBlock.publicMethod("JsonWriter toJson(JsonWriter jsonWriter) throws IOException", methodBlock -> { - if (isJsonMergePatch) { - // If the model is the root parent use the JSON merge patch serialization tracking property directly, - // otherwise use the access helper to determine whether to use JSON merge patch serialization. - ClientModel rootParent = ClientModelUtil.getRootParent(propertiesManager.getModel()); - String ifStatement = (rootParent == propertiesManager.getModel()) - ? "jsonMergePatch" - : JSON_MERGE_PATCH_HELPER_CLASS_NAME + ".get" + rootParent.getName() + "Accessor().isJsonMergePatch(this)"; - - methodBlock.ifBlock(ifStatement, ifBlock -> ifBlock.methodReturn("toJsonMergePatch(jsonWriter)")) - .elseBlock(elseBlock -> serializeJsonProperties(methodBlock, propertiesManager, false, false, false)); - } else { - // For polymorphic models, when all models are contained in the same package, a package-private 'toJsonShared' - // method will be generated by parent models that handles serialization of properties defined by the parent - // model. - // So, if we have that case, we skip serializing the properties defined by the parent model(s) and instead call - // the 'toJsonShared' method to serialize those properties. And, for properties defined by this model, if the - // model is a parent model generate a 'toJsonShared' method to serialize those properties. - // - // At this time, due to implementation complexity and the low value add, JSON merge patch serialization won't - // support 'toJsonMergePatchShared'. JSON merge patch serialization has low usage, and combined with the low - // usage of polymorphism as well, there isn't a large expectation for this feature. - serializeJsonProperties(methodBlock, propertiesManager, false, callToJsonSharedForParentProperties, - callToJsonSharedForThisProperties); - } - }); - - // If this model is defining a 'toJsonShared' method, write it. - if (callToJsonSharedForThisProperties) { - classBlock.packagePrivateMethod("void toJsonShared(JsonWriter jsonWriter) throws IOException", methodBlock -> { - if (callToJsonSharedForParentProperties) { - methodBlock.line("super.toJsonShared(jsonWriter);"); - } + protected boolean isManagementErrorSubclass(ClientModel model) { + return false; + } - serializeThisJsonProperties(propertiesManager, (property, fromSuper) -> serializeJsonProperty( - methodBlock, property, property.getSerializedName(), fromSuper, true, false), methodBlock, - callToJsonSharedForParentProperties); - }); + @Override + protected void writeStreamStyleSerialization(JavaClass classBlock, ClientModelPropertiesManager propertiesManager) { + // Early out as strongly-typed headers do their own thing. + if (propertiesManager.getModel().isStronglyTypedHeader()) { + return; } - } - /** - * write toJsonMergePatch() method - * - * @param classBlock The class block to write the toJsonMergePatch method to. - * @param propertiesManager The properties manager for the model. - * @param addGeneratedAnnotation The consumer to add the generated annotation to the class block. - */ - private static void writeToJsonMergePatch(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - Consumer addGeneratedAnnotation) { - addGeneratedAnnotation.accept(classBlock); - classBlock.privateMethod("JsonWriter toJsonMergePatch(JsonWriter jsonWriter) throws IOException", - methodBlock -> serializeJsonProperties(methodBlock, propertiesManager, true, false, false)); + new StreamSerializationGenerator(propertiesManager, this::isManagementErrorSubclass) + .writeStreamStyleSerialization(classBlock); } - /** - * Serializes the properties of a model to JSON. - * @param methodBlock The method block to write the serialization method to. - * @param propertiesManager The properties manager for the model. - * @param isJsonMergePatch Whether the serialization is for a JSON merge patch. - */ - private static void serializeJsonProperties(JavaBlock methodBlock, ClientModelPropertiesManager propertiesManager, - boolean isJsonMergePatch, boolean callToJsonSharedForParentProperties, - boolean callToJsonSharedForThisProperties) { - methodBlock.line("jsonWriter.writeStartObject();"); - - // If we're calling toJsonShared for this model and for parent properties, this is an early out. - if (callToJsonSharedForParentProperties && callToJsonSharedForThisProperties) { - methodBlock.line("toJsonShared(jsonWriter);"); - methodBlock.methodReturn("jsonWriter.writeEndObject()"); - return; - } + private static final class StreamSerializationGenerator { + private final ClientModelPropertiesManager propertiesManager; + private final ClientModel model; + private final JavaSettings settings; + private final Predicate isManagementErrorSubclass; - BiConsumer serializeJsonProperty = (property, fromSuper) -> serializeJsonProperty( - methodBlock, property, property.getSerializedName(), fromSuper, true, isJsonMergePatch); + private final Consumer addGeneratedAnnotation; + private final boolean isJsonMergePatchModel; + private final boolean useFromJsonShared; - if (callToJsonSharedForParentProperties) { - methodBlock.line("toJsonShared(jsonWriter);"); - } else { - serializeParentJsonProperties(propertiesManager, serializeJsonProperty); - } + private StreamSerializationGenerator(ClientModelPropertiesManager propertiesManager, + Predicate isManagementErrorSubclass) { + this.propertiesManager = propertiesManager; + this.model = propertiesManager.getModel(); + this.settings = propertiesManager.getSettings(); + this.isManagementErrorSubclass = isManagementErrorSubclass; - if (callToJsonSharedForThisProperties) { - methodBlock.line("toJsonShared(jsonWriter);"); - } else { - serializeThisJsonProperties(propertiesManager, serializeJsonProperty, methodBlock, - callToJsonSharedForParentProperties); + this.addGeneratedAnnotation = Templates.getModelTemplate()::addGeneratedAnnotation; + this.isJsonMergePatchModel = ClientModelUtil.isJsonMergePatchModel(model, settings); + this.useFromJsonShared = canUseFromJsonShared(propertiesManager); } + private void writeStreamStyleSerialization(JavaClass classBlock) { + if (model.getXmlName() != null) { + writeToXml(classBlock); + if (isSuperTypeWithDiscriminator(model)) { + writeSuperTypeFromXml(classBlock); + } else { + writeTerminalTypeFromXml(classBlock); + } + } else { + if (isJsonMergePatchModel) { + writeToJson(classBlock, true); + addGeneratedAnnotation.accept(classBlock); + classBlock.privateMethod("JsonWriter toJsonMergePatch(JsonWriter jsonWriter) throws IOException", + methodBlock -> serializeJsonProperties(methodBlock, true, false, false)); + } else { + writeToJson(classBlock, false); + } - methodBlock.methodReturn("jsonWriter.writeEndObject()"); - } - - private static void serializeParentJsonProperties(ClientModelPropertiesManager propertiesManager, - BiConsumer serializeJsonProperty) { - propertiesManager.getModel() - .getParentPolymorphicDiscriminators() - .forEach(discriminator -> serializeJsonProperty.accept(discriminator, false)); - propertiesManager.forEachSuperRequiredProperty(property -> serializeJsonProperty.accept(property, true)); - propertiesManager.forEachSuperSetterProperty(property -> serializeJsonProperty.accept(property, true)); - } + // All classes will create a public fromJson(JsonReader) method that initiates reading. + // How the implementation looks depends on whether the type is a super type, subtype, both, or is a + // stand-alone type. + // + // Intermediate types, those that are both a super type and subtype, will pass null as the type discriminator + // value. This is done as super types are written to only support themselves or their subtypes, passing a + // discriminator into its own super type would confuse this scenario. For example, one of the test Swaggers + // generates the following hierarchy + // + // Fish + // Salmon Shark + // SmartSalmon Sawshark GoblinShark Cookiecuttershark + // + // If Salmon called into Fish with its discriminator and an error occurred it would mention the Shark subtypes + // as potential legal values for deserialization, confusing the Salmon deserializer. So, calling into Salmon + // will only attempt to handle Salmon and SmartSalmon, and same goes for Shark with Shark, Sawshark, + // GoblinShark, and Cookiecuttershark. It's only Fish that will handle all subtypes, as this is the most generic + // super type. This also creates a well-defined bounds for code generation in regard to type hierarchies. + // + // In a real scenario someone deserializing to Salmon should have an exception about discriminator types + // if the JSON payload is a Shark and not get a ClassCastException as it'd be more confusing on why a Shark + // was trying to be converted to a Salmon. + if (isSuperTypeWithDiscriminator(model)) { + writeSuperTypeFromJson(classBlock); + } else { + readJsonObject(classBlock, false, this::writeFromJsonDeserialization); + } - private static void serializeThisJsonProperties(ClientModelPropertiesManager propertiesManager, - BiConsumer serializeJsonProperty, JavaBlock methodBlock, - boolean callToJsonSharedForParentProperties) { - Consumer wrappedSerializer = property -> { - // Skip serializing the polymorphic discriminator if 'toJsonShared' is called to serialize properties - // defined by the parent model(s), the polymorphic discriminator isn't defined by this property, and - // all polymorphic models are in the same package. - // In this scenario, the logic for polymorphism is that the defining model has a package-private, non-final - // field for the discriminator which is set by either the deserialization logic or the constructor. - if (callToJsonSharedForParentProperties && property.isPolymorphicDiscriminator() - && propertiesManager.getModel().isAllPolymorphicModelsInSamePackage() - && !propertiesManager.getModel().isPolymorphicDiscriminatorDefinedByModel()) { - return; + if (isManagementErrorSubclass.test(model)) { + addGeneratedAnnotation.accept(classBlock); + classBlock.staticMethod(JavaVisibility.Private, model.getName() + " " + + READ_MANAGEMENT_ERROR_METHOD_NAME + "(JsonReader jsonReader) throws IOException", + methodBlock -> readJsonObjectMethodBody(methodBlock, this::writeFromJsonDeserialization0)); + } } + } - serializeJsonProperty.accept(property, false); - }; - - propertiesManager.forEachRequiredProperty(wrappedSerializer); - propertiesManager.forEachSetterProperty(wrappedSerializer); - - handleFlattenedPropertiesSerialization(methodBlock, propertiesManager.getJsonFlattenedPropertiesTree(), false, - callToJsonSharedForParentProperties); - - if (getAdditionalPropertiesPropertyInModelOrFromSuper(propertiesManager) != null) { - String additionalPropertiesAccessExpr = propertiesManager.getAdditionalProperties() != null - ? propertiesManager.getAdditionalProperties().getName() - : propertiesManager.getSuperAdditionalPropertiesProperty().getGetterName() + "()"; - IType wireType = propertiesManager.getAdditionalProperties() != null - ? propertiesManager.getAdditionalProperties().getWireType() - : propertiesManager.getSuperAdditionalPropertiesProperty().getWireType(); - - methodBlock.ifBlock(additionalPropertiesAccessExpr + " != null", ifAction -> { - IType valueType = ((MapType) wireType).getValueType().asNullable(); - ifAction.line("for (Map.Entry additionalProperty : %s.entrySet()) {", valueType, additionalPropertiesAccessExpr); - ifAction.indent(() -> { - if (valueType == ClassType.BINARY_DATA) { - // Special handling for BinaryData - ifAction.line("jsonWriter.writeUntypedField(additionalProperty.getKey(), additionalProperty.getValue() == null ? null : additionalProperty.getValue().toObject(Object.class));"); - } else { - ifAction.line("jsonWriter.writeUntypedField(additionalProperty.getKey(), additionalProperty.getValue());"); - } - }); - ifAction.line("}"); + /** + * write toJson() method. + *

+ * If it is a JsonMergePatch model, toJson() should first check jsonMergePatch flag value and then do + * serialization accordingly. + * + * @param classBlock The class block to write the toJson method to. + * @param isJsonMergePatch Whether the serialization is for a JSON merge patch. + */ + private void writeToJson(JavaClass classBlock, boolean isJsonMergePatch) { + boolean callToJsonSharedForParentProperties = settings.isShareJsonSerializableCode() && !isJsonMergePatch + && model.isAllPolymorphicModelsInSamePackage() && !CoreUtils.isNullOrEmpty(model.getParentModelName()); + boolean callToJsonSharedForThisProperties = settings.isShareJsonSerializableCode() && !isJsonMergePatch + && model.isAllPolymorphicModelsInSamePackage() && model.isPolymorphicParent(); + + classBlock.javadocComment(JavaJavadocComment::inheritDoc); + addGeneratedAnnotation.accept(classBlock); + classBlock.annotation("Override"); + classBlock.publicMethod("JsonWriter toJson(JsonWriter jsonWriter) throws IOException", methodBlock -> { + if (isJsonMergePatch) { + // If the model is the root parent use the JSON merge patch serialization tracking property directly, + // otherwise use the access helper to determine whether to use JSON merge patch serialization. + ClientModel rootParent = ClientModelUtil.getRootParent(model); + String ifStatement = (rootParent == model) + ? "jsonMergePatch" + : JSON_MERGE_PATCH_HELPER_CLASS_NAME + ".get" + rootParent.getName() + + "Accessor().isJsonMergePatch(this)"; + + methodBlock.ifBlock(ifStatement, ifBlock -> ifBlock.methodReturn("toJsonMergePatch(jsonWriter)")) + .elseBlock(elseBlock -> serializeJsonProperties(methodBlock, false, false, false)); + } else { + // For polymorphic models, when all models are contained in the same package, a package-private 'toJsonShared' + // method will be generated by parent models that handles serialization of properties defined by the parent + // model. + // So, if we have that case, we skip serializing the properties defined by the parent model(s) and instead call + // the 'toJsonShared' method to serialize those properties. And, for properties defined by this model, if the + // model is a parent model generate a 'toJsonShared' method to serialize those properties. + // + // At this time, due to implementation complexity and the low value add, JSON merge patch serialization won't + // support 'toJsonMergePatchShared'. JSON merge patch serialization has low usage, and combined with the low + // usage of polymorphism as well, there isn't a large expectation for this feature. + serializeJsonProperties(methodBlock, false, callToJsonSharedForParentProperties, + callToJsonSharedForThisProperties); + } }); - } - } - /** - * Serializes a non-flattened, non-additional properties JSON property. - *

- * If the JSON property needs to be flattened or is additional properties this is a no-op as those require special - * handling that will occur later. - * - * @param methodBlock The method handling serialization. - * @param property The property being serialized. - * @param serializedName The serialized JSON property name. Generally, this is just the {@code property property's} - * serialized name but if a flattened property is being serialized it'll be the last segment of the flattened JSON - * name. - * @param fromSuperType Whether the property is defined by a super type of the model. If the property is declared by - * a super type a getter method will be used to retrieve the value instead of accessing the field directly. - * @param ignoreFlattening Whether flattened properties should be skipped. Will only be false when handling the - * terminal location of a flattened structure. - * @param isJsonMergePatch Whether the serialization is for a JSON Merge Patch model. - */ - private static void serializeJsonProperty(JavaBlock methodBlock, ClientModelProperty property, - String serializedName, boolean fromSuperType, boolean ignoreFlattening, boolean isJsonMergePatch) { - if ((ignoreFlattening && property.getNeedsFlatten()) || property.isAdditionalProperties()) { - // Property will be handled later by flattened or additional properties serialization. - return; - } + // If this model is defining a 'toJsonShared' method, write it. + if (callToJsonSharedForThisProperties) { + classBlock.packagePrivateMethod("void toJsonShared(JsonWriter jsonWriter) throws IOException", + methodBlock -> { + if (callToJsonSharedForParentProperties) { + methodBlock.line("super.toJsonShared(jsonWriter);"); + } - if (property.isReadOnly() && !property.isPolymorphicDiscriminator()) { - // Non-polymorphic discriminator, readonly properties are never serialized. - return; + serializeThisJsonProperties( + (property, fromSuper) -> serializeJsonProperty(methodBlock, property, + property.getSerializedName(), fromSuper, true, false), methodBlock, + callToJsonSharedForParentProperties); + }); + } } - if (isJsonMergePatch) { - if (!property.isPolymorphicDiscriminator()) { - methodBlock.ifBlock("updatedProperties.contains(\"" + property.getName() + "\")", codeBlock -> { - if (property.getClientType().isNullable()) { - codeBlock.ifBlock(getPropertyGetterStatement(property, fromSuperType) + " == null", - ifBlock -> ifBlock.line("jsonWriter.writeNullField(\"" + property.getSerializedName() + "\");")) - .elseBlock(elseBlock -> serializeJsonProperty(codeBlock, property, serializedName, fromSuperType, true)); - } else { - serializeJsonProperty(codeBlock, property, serializedName, fromSuperType, true, false); - } - }); - } else { - serializeJsonProperty(methodBlock, property, serializedName, fromSuperType, true); + /** + * Serializes the properties of a model to JSON. + * + * @param methodBlock The method block to write the serialization method to. + * @param isJsonMergePatch Whether the serialization is for a JSON merge patch. + */ + private void serializeJsonProperties(JavaBlock methodBlock, boolean isJsonMergePatch, + boolean callToJsonSharedForParentProperties, boolean callToJsonSharedForThisProperties) { + methodBlock.line("jsonWriter.writeStartObject();"); + + // If we're calling toJsonShared for this model and for parent properties, this is an early out. + if (callToJsonSharedForParentProperties && callToJsonSharedForThisProperties) { + methodBlock.line("toJsonShared(jsonWriter);"); + methodBlock.methodReturn("jsonWriter.writeEndObject()"); + return; } - } else { - serializeJsonProperty(methodBlock, property, serializedName, fromSuperType, false); - } - } - /** - * Serializes a non-flattened, non-additional properties JSON property. - *

- * If the JSON property needs to be flattened or is additional properties this is a no-op as those require special - * handling that will occur later. - * - * @param methodBlock The method handling serialization. - * @param property The property being serialized. - * @param serializedName The serialized JSON property name. Generally, this is just the {@code property property's} - * serialized name but if a flattened property is being serialized it'll be the last segment of the flattened JSON - * name. - * @param fromSuperType Whether the property is defined by a super type of the model. If the property is declared by - * a super type a getter method will be used to retrieve the value instead of accessing the field directly. - * @param isJsonMergePatch Whether the serialization is for a JSON Merge Patch model. - */ - private static void serializeJsonProperty(JavaBlock methodBlock, ClientModelProperty property, - String serializedName, boolean fromSuperType, boolean isJsonMergePatch) { - IType clientType = property.getClientType(); - IType wireType = property.getWireType(); - String propertyValueGetter = getPropertyGetterStatement(property, fromSuperType); - - // Attempt to determine whether the wire type is simple serialization. - // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. - String fieldSerializationMethod = wireType.jsonSerializationMethodCall("jsonWriter", serializedName, - propertyValueGetter, isJsonMergePatch); - if (wireType == ClassType.BINARY_DATA) { - // Special handling for BinaryData (instead of using "serializationMethodBase" and "serializationValueGetterModifier") - // The reason is that some backend would fail the request on "null" value (e.g. OpenAI) - String writeBinaryDataExpr = "jsonWriter.writeUntypedField(\"" + serializedName + "\", " + propertyValueGetter + ".toObject(Object.class));"; - if (!property.isRequired()) { - methodBlock.ifBlock(propertyValueGetter + " != null", ifAction -> ifAction.line(writeBinaryDataExpr)); + BiConsumer serializeJsonProperty + = (property, fromSuper) -> serializeJsonProperty(methodBlock, property, property.getSerializedName(), + fromSuper, true, isJsonMergePatch); + + if (callToJsonSharedForParentProperties) { + methodBlock.line("toJsonShared(jsonWriter);"); } else { - methodBlock.line(writeBinaryDataExpr); - } - } else if (fieldSerializationMethod != null) { - if (isJsonMergePatch && wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { - methodBlock.line("JsonMergePatchHelper.get" + clientType.toString() + "Accessor().prepareModelForJsonMergePatch(" + propertyValueGetter + ", true);"); + serializeParentJsonProperties(serializeJsonProperty); } - if (fromSuperType && clientType != wireType && clientType.isNullable()) { - // If the property is from a super type and the client type is different from the wire type then a null - // check is required to prevent a NullPointerException when converting the value. - methodBlock.ifBlock(property.getGetterName() + "() != null", - ifAction -> ifAction.line(fieldSerializationMethod + ";")); + + if (callToJsonSharedForThisProperties) { + methodBlock.line("toJsonShared(jsonWriter);"); } else { - methodBlock.line(fieldSerializationMethod + ";"); - } - if (isJsonMergePatch && wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { - methodBlock.line("JsonMergePatchHelper.get" + clientType.toString() + "Accessor().prepareModelForJsonMergePatch(" + propertyValueGetter + ", false);"); + serializeThisJsonProperties(serializeJsonProperty, methodBlock, callToJsonSharedForParentProperties); } - } else if (wireType == ClassType.OBJECT) { - methodBlock.line("jsonWriter.writeUntypedField(\"" + serializedName + "\", " + propertyValueGetter + ");"); - } else if (wireType instanceof IterableType) { - serializeJsonContainerProperty(methodBlock, "writeArrayField", wireType, ((IterableType) wireType).getElementType(), - serializedName, propertyValueGetter, 0, isJsonMergePatch); - } else if (wireType instanceof MapType) { - // Assumption is that the key type for the Map is a String. This may not always hold true and when that - // becomes reality this will need to be reworked to handle that case. - serializeJsonContainerProperty(methodBlock, "writeMapField", wireType, ((MapType) wireType).getValueType(), - serializedName, propertyValueGetter, 0, isJsonMergePatch); - } else { - // TODO (alzimmer): Resolve this as deserialization logic generation needs to handle all cases. - throw new RuntimeException("Unknown wire type " + wireType + " in serialization. Need to add support for it."); - } - } - /** - * Helper function to get property getter statement. - *

- * If the value is from super type, then we will return "getProperty()", otherwise, return "this.property" - * - * @param property The property being serialized. - * @param fromSuperType Whether the property is defined by a super type of the model. - * @return The property getter statement. - */ - private static String getPropertyGetterStatement(ClientModelProperty property, boolean fromSuperType) { - IType clientType = property.getClientType(); - IType wireType = property.getWireType(); - if (fromSuperType) { - return (clientType != wireType) - ? wireType.convertFromClientType(property.getGetterName() + "()") : property.getGetterName() + "()"; - } else { - return "this." + property.getName(); + methodBlock.methodReturn("jsonWriter.writeEndObject()"); } - } - /** - * Helper method to serialize a JSON container property (such as {@link List} and {@link Map}). - * - * @param methodBlock The method handling serialization. - * @param utilityMethod The method aiding in the serialization of the container. - * @param containerType The container type. - * @param elementType The element type for the container, for a {@link List} this is the element type and for a - * {@link Map} this is the value type. - * @param serializedName The serialized property name. - * @param propertyValueGetter The property or property getter for the field being serialized. - * @param depth Depth of recursion for container types, such as {@code Map>} would be 0 when - * {@code Map} is being handled and then 1 when {@code List} is being handled. - * @param isJsonMergePatch Whether the serialization is for a JSON Merge Patch model. - */ - private static void serializeJsonContainerProperty(JavaBlock methodBlock, String utilityMethod, IType containerType, - IType elementType, String serializedName, String propertyValueGetter, int depth, boolean isJsonMergePatch) { - String callingWriterName = depth == 0 ? "jsonWriter" : (depth == 1) ? "writer" : "writer" + (depth - 1); - String lambdaWriterName = depth == 0 ? "writer" : "writer" + depth; - String elementName = depth == 0 ? "element" : "element" + depth; - String valueSerializationMethod = elementType.jsonSerializationMethodCall(lambdaWriterName, null, elementName, - isJsonMergePatch); - String serializeValue = depth == 0 ? propertyValueGetter - : ((depth == 1) ? "element" : "element" + (depth - 1)); - - // First call into serialize container property will need to write the property name. Subsequent calls must - // not write the property name as that would be invalid, ex "myList":["myList":["innerListElement"]]. - if (depth == 0) { - // Container property shouldn't be written if it's null. - methodBlock.line("%s.%s(\"%s\", %s, (%s, %s) -> ", callingWriterName, utilityMethod, serializedName, - serializeValue, lambdaWriterName, elementName); - } else { - // But the inner container should be written if it's null. - methodBlock.line("%s.%s(%s, (%s, %s) -> ", callingWriterName, utilityMethod, serializeValue, - lambdaWriterName, elementName); - } + private void serializeParentJsonProperties(BiConsumer serializeJsonProperty) { + model.getParentPolymorphicDiscriminators() + .forEach(discriminator -> serializeJsonProperty.accept(discriminator, false)); + propertiesManager.forEachSuperRequiredProperty(property -> serializeJsonProperty.accept(property, true)); + propertiesManager.forEachSuperSetterProperty(property -> serializeJsonProperty.accept(property, true)); + } + + private void serializeThisJsonProperties(BiConsumer serializeJsonProperty, + JavaBlock methodBlock, boolean callToJsonSharedForParentProperties) { + Consumer wrappedSerializer = property -> { + // Skip serializing the polymorphic discriminator if 'toJsonShared' is called to serialize properties + // defined by the parent model(s), the polymorphic discriminator isn't defined by this property, and + // all polymorphic models are in the same package. + // In this scenario, the logic for polymorphism is that the defining model has a package-private, non-final + // field for the discriminator which is set by either the deserialization logic or the constructor. + if (callToJsonSharedForParentProperties && property.isPolymorphicDiscriminator() + && model.isAllPolymorphicModelsInSamePackage() + && !model.isPolymorphicDiscriminatorDefinedByModel()) { + return; + } + + serializeJsonProperty.accept(property, false); + }; + + propertiesManager.forEachRequiredProperty(wrappedSerializer); + propertiesManager.forEachSetterProperty(wrappedSerializer); + + handleFlattenedPropertiesSerialization(methodBlock, callToJsonSharedForParentProperties); + + if (getAdditionalPropertiesPropertyInModelOrFromSuper() != null) { + String additionalPropertiesAccessExpr = propertiesManager.getAdditionalProperties() != null + ? propertiesManager.getAdditionalProperties().getName() + : propertiesManager.getSuperAdditionalPropertiesProperty().getGetterName() + "()"; + IType wireType = propertiesManager.getAdditionalProperties() != null + ? propertiesManager.getAdditionalProperties().getWireType() + : propertiesManager.getSuperAdditionalPropertiesProperty().getWireType(); - methodBlock.indent(() -> { - if (valueSerializationMethod != null) { - if (isJsonMergePatch && containerType instanceof MapType) { - methodBlock.block("", codeBlock -> codeBlock.ifBlock(elementName + "!= null", ifBlock -> { - if (elementType instanceof ClassType && ((ClassType) elementType).isSwaggerType()) { - methodBlock.line("JsonMergePatchHelper.get" + ((ClassType) elementType).getName() + "Accessor().prepareModelForJsonMergePatch(" + elementName + ", true);"); + methodBlock.ifBlock(additionalPropertiesAccessExpr + " != null", ifAction -> { + IType valueType = ((MapType) wireType).getValueType().asNullable(); + ifAction.line("for (Map.Entry additionalProperty : %s.entrySet()) {", valueType, + additionalPropertiesAccessExpr); + ifAction.indent(() -> { + if (valueType == ClassType.BINARY_DATA) { + // Special handling for BinaryData + ifAction.line( + "jsonWriter.writeUntypedField(additionalProperty.getKey(), additionalProperty.getValue() == null ? null : additionalProperty.getValue().toObject(Object.class));"); + } else { + ifAction.line( + "jsonWriter.writeUntypedField(additionalProperty.getKey(), additionalProperty.getValue());"); } - ifBlock.line(valueSerializationMethod + ";"); - if (elementType instanceof ClassType && ((ClassType) elementType).isSwaggerType()) { - methodBlock.line("JsonMergePatchHelper.get" + ((ClassType) elementType).getName() + "Accessor().prepareModelForJsonMergePatch(" + elementName + ", false);"); + }); + ifAction.line("}"); + }); + } + } + + /** + * Serializes a non-flattened, non-additional properties JSON property. + *

+ * If the JSON property needs to be flattened or is additional properties this is a no-op as those require + * special handling that will occur later. + * + * @param methodBlock The method handling serialization. + * @param property The property being serialized. + * @param serializedName The serialized JSON property name. Generally, this is just the + * {@code property property's} serialized name but if a flattened property is being serialized it'll be the last + * segment of the flattened JSON name. + * @param fromSuperType Whether the property is defined by a super type of the model. If the property is + * declared by a super type a getter method will be used to retrieve the value instead of accessing the field + * directly. + * @param ignoreFlattening Whether flattened properties should be skipped. Will only be false when handling the + * terminal location of a flattened structure. + * @param isJsonMergePatch Whether the serialization is for a JSON Merge Patch model. + */ + private static void serializeJsonProperty(JavaBlock methodBlock, ClientModelProperty property, + String serializedName, boolean fromSuperType, boolean ignoreFlattening, boolean isJsonMergePatch) { + if ((ignoreFlattening && property.getNeedsFlatten()) || property.isAdditionalProperties()) { + // Property will be handled later by flattened or additional properties serialization. + return; + } + + if (property.isReadOnly() && !property.isPolymorphicDiscriminator()) { + // Non-polymorphic discriminator, readonly properties are never serialized. + return; + } + + if (isJsonMergePatch) { + if (!property.isPolymorphicDiscriminator()) { + methodBlock.ifBlock("updatedProperties.contains(\"" + property.getName() + "\")", codeBlock -> { + if (property.getClientType().isNullable()) { + codeBlock.ifBlock(getPropertyGetterStatement(property, fromSuperType) + " == null", + ifBlock -> ifBlock.line( + "jsonWriter.writeNullField(\"" + property.getSerializedName() + "\");")) + .elseBlock(elseBlock -> serializeJsonProperty(codeBlock, property, serializedName, + fromSuperType, true)); + } else { + serializeJsonProperty(codeBlock, property, serializedName, fromSuperType, true, false); } - }).elseBlock(elseBlock -> elseBlock.line(lambdaWriterName + ".writeNull();"))); + }); + } else { + serializeJsonProperty(methodBlock, property, serializedName, fromSuperType, true); + } + } else { + serializeJsonProperty(methodBlock, property, serializedName, fromSuperType, false); + } + } + + /** + * Serializes a non-flattened, non-additional properties JSON property. + *

+ * If the JSON property needs to be flattened or is additional properties this is a no-op as those require + * special handling that will occur later. + * + * @param methodBlock The method handling serialization. + * @param property The property being serialized. + * @param serializedName The serialized JSON property name. Generally, this is just the + * {@code property property's} serialized name but if a flattened property is being serialized it'll be the last + * segment of the flattened JSON name. + * @param fromSuperType Whether the property is defined by a super type of the model. If the property is + * declared by a super type a getter method will be used to retrieve the value instead of accessing the field + * directly. + * @param isJsonMergePatch Whether the serialization is for a JSON Merge Patch model. + */ + private static void serializeJsonProperty(JavaBlock methodBlock, ClientModelProperty property, + String serializedName, boolean fromSuperType, boolean isJsonMergePatch) { + IType clientType = property.getClientType(); + IType wireType = property.getWireType(); + String propertyValueGetter = getPropertyGetterStatement(property, fromSuperType); + + // Attempt to determine whether the wire type is simple serialization. + // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. + String fieldSerializationMethod = wireType.jsonSerializationMethodCall("jsonWriter", serializedName, + propertyValueGetter, isJsonMergePatch); + if (wireType == ClassType.BINARY_DATA) { + // Special handling for BinaryData (instead of using "serializationMethodBase" and "serializationValueGetterModifier") + // The reason is that some backend would fail the request on "null" value (e.g. OpenAI) + String writeBinaryDataExpr = "jsonWriter.writeUntypedField(\"" + serializedName + "\", " + + propertyValueGetter + ".toObject(Object.class));"; + if (!property.isRequired()) { + methodBlock.ifBlock(propertyValueGetter + " != null", + ifAction -> ifAction.line(writeBinaryDataExpr)); + } else { + methodBlock.line(writeBinaryDataExpr); + } + } else if (fieldSerializationMethod != null) { + if (isJsonMergePatch && wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { + methodBlock.line( + "JsonMergePatchHelper.get" + clientType.toString() + "Accessor().prepareModelForJsonMergePatch(" + + propertyValueGetter + ", true);"); + } + if (fromSuperType && clientType != wireType && clientType.isNullable()) { + // If the property is from a super type and the client type is different from the wire type then a null + // check is required to prevent a NullPointerException when converting the value. + methodBlock.ifBlock(property.getGetterName() + "() != null", + ifAction -> ifAction.line(fieldSerializationMethod + ";")); } else { - methodBlock.line(valueSerializationMethod); + methodBlock.line(fieldSerializationMethod + ";"); } - } else if (elementType == ClassType.OBJECT) { - methodBlock.line(lambdaWriterName + ".writeUntyped(" + elementName + ")"); - } else if (elementType instanceof IterableType) { - serializeJsonContainerProperty(methodBlock, "writeArray", elementType, ((IterableType) elementType).getElementType(), - serializedName, propertyValueGetter, depth + 1, isJsonMergePatch); - } else if (elementType instanceof MapType) { + if (isJsonMergePatch && wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { + methodBlock.line( + "JsonMergePatchHelper.get" + clientType.toString() + "Accessor().prepareModelForJsonMergePatch(" + + propertyValueGetter + ", false);"); + } + } else if (wireType == ClassType.OBJECT) { + methodBlock.line( + "jsonWriter.writeUntypedField(\"" + serializedName + "\", " + propertyValueGetter + ");"); + } else if (wireType instanceof IterableType) { + serializeJsonContainerProperty(methodBlock, "writeArrayField", wireType, + ((IterableType) wireType).getElementType(), serializedName, propertyValueGetter, 0, + isJsonMergePatch); + } else if (wireType instanceof MapType) { // Assumption is that the key type for the Map is a String. This may not always hold true and when that // becomes reality this will need to be reworked to handle that case. - serializeJsonContainerProperty(methodBlock, "writeMap", elementType, ((MapType) elementType).getValueType(), - serializedName, propertyValueGetter, depth + 1, isJsonMergePatch); - } else if (elementType == ClassType.BINARY_DATA) { - methodBlock.line(lambdaWriterName + ".writeUntyped(" + elementName + ")"); + serializeJsonContainerProperty(methodBlock, "writeMapField", wireType, + ((MapType) wireType).getValueType(), serializedName, propertyValueGetter, 0, isJsonMergePatch); } else { - throw new RuntimeException("Unknown value type " + elementType + " in " + containerType - + " serialization. Need to add support for it."); + // TODO (alzimmer): Resolve this as deserialization logic generation needs to handle all cases. + throw new RuntimeException( + "Unknown wire type " + wireType + " in serialization. Need to add support for it."); } - }); - - if (depth > 0) { - methodBlock.line(")"); - } else { - methodBlock.line(");"); } - } - /** - * Helper method to serialize flattened properties in a model. - *

- * Flattened properties are unique as for each level of flattening they'll create a JSON sub-object. But before a - * sub-object is created any field needs to be checked for either being a primitive value or non-null. Primitive - * values are usually serialized no matter their value so those will automatically trigger the JSON sub-object to be - * created, nullable values will be checked for being non-null. - *

- * In addition to primitive or non-null checking fields, all properties from the same JSON sub-object must be - * written at the same time to prevent an invalid JSON structure. For example if a model has three flattened - * properties with JSON paths "im.flattened", "im.deeper.flattened", and "im.deeper.flattenedtoo" this will create - * the following structure: - * - *

-   * im -> flattened
-   *     | deeper -> flattened
-   *               | flattenedtoo
-   * 
- * - * So, "im.deeper.flattened" and "im.deeper.flattenedtoo" will need to be serialized at the same time to get the - * correct JSON where there is only one "im: deeper" JSON sub-object. - * - * @param methodBlock The method handling serialization. - * @param flattenedProperties The flattened properties structure. - * @param isJsonMergePatch Whether the serialization is for a JSON merge patch model. - */ - private static void handleFlattenedPropertiesSerialization(JavaBlock methodBlock, - JsonFlattenedPropertiesTree flattenedProperties, boolean isJsonMergePatch, - boolean callToJsonSharedForParentProperties) { - // The initial call to handle flattened properties is using the base node which is just a holder. - for (JsonFlattenedPropertiesTree flattened : flattenedProperties.getChildrenNodes().values()) { - handleFlattenedPropertiesSerializationHelper(methodBlock, flattened, isJsonMergePatch, - callToJsonSharedForParentProperties); + /** + * Helper function to get property getter statement. + *

+ * If the value is from super type, then we will return "getProperty()", otherwise, return "this.property" + * + * @param property The property being serialized. + * @param fromSuperType Whether the property is defined by a super type of the model. + * @return The property getter statement. + */ + private static String getPropertyGetterStatement(ClientModelProperty property, boolean fromSuperType) { + IType clientType = property.getClientType(); + IType wireType = property.getWireType(); + if (fromSuperType) { + return (clientType != wireType) + ? wireType.convertFromClientType(property.getGetterName() + "()") + : property.getGetterName() + "()"; + } else { + return "this." + property.getName(); + } } - } - private static void handleFlattenedPropertiesSerializationHelper(JavaBlock methodBlock, - JsonFlattenedPropertiesTree flattenedProperties, boolean isJsonMergePatch, - boolean callToJsonSharedForParentProperties) { - ClientModelPropertyWithMetadata flattenedProperty = flattenedProperties.getProperty(); - if (flattenedProperty != null) { - // This is a terminal location, only need to add property serialization. - serializeJsonProperty(methodBlock, flattenedProperty.getProperty(), flattenedProperties.getNodeName(), - flattenedProperty.isFromSuperClass(), false, isJsonMergePatch); - } else { - // Otherwise this is an intermediate location. - // Check for either any of the properties in this subtree being primitives or add an if block checking that - // any of the properties are non-null. - List propertiesInFlattenedGroup = - getClientModelPropertiesInJsonTree(flattenedProperties); - boolean hasPrimitivePropertyInGroup = false; - boolean allPropertiesInGroupFromParent = true; - for (ClientModelPropertyWithMetadata propertyInFlattenedGroup : propertiesInFlattenedGroup) { - hasPrimitivePropertyInGroup |= propertyInFlattenedGroup.getProperty().getWireType() instanceof PrimitiveType; - allPropertiesInGroupFromParent &= propertyInFlattenedGroup.isFromSuperClass(); + /** + * Helper method to serialize a JSON container property (such as {@link List} and {@link Map}). + * + * @param methodBlock The method handling serialization. + * @param utilityMethod The method aiding in the serialization of the container. + * @param containerType The container type. + * @param elementType The element type for the container, for a {@link List} this is the element type and for a + * {@link Map} this is the value type. + * @param serializedName The serialized property name. + * @param propertyValueGetter The property or property getter for the field being serialized. + * @param depth Depth of recursion for container types, such as {@code Map>} would be 0 + * when {@code Map} is being handled and then 1 when {@code List} is being handled. + * @param isJsonMergePatch Whether the serialization is for a JSON Merge Patch model. + */ + private static void serializeJsonContainerProperty(JavaBlock methodBlock, String utilityMethod, + IType containerType, IType elementType, String serializedName, String propertyValueGetter, int depth, + boolean isJsonMergePatch) { + String callingWriterName = depth == 0 ? "jsonWriter" : (depth == 1) ? "writer" : "writer" + (depth - 1); + String lambdaWriterName = depth == 0 ? "writer" : "writer" + depth; + String elementName = depth == 0 ? "element" : "element" + depth; + String valueSerializationMethod = elementType.jsonSerializationMethodCall(lambdaWriterName, null, + elementName, isJsonMergePatch); + String serializeValue = depth == 0 + ? propertyValueGetter + : ((depth == 1) ? "element" : "element" + (depth - 1)); + + // First call into serialize container property will need to write the property name. Subsequent calls must + // not write the property name as that would be invalid, ex "myList":["myList":["innerListElement"]]. + if (depth == 0) { + // Container property shouldn't be written if it's null. + methodBlock.line("%s.%s(\"%s\", %s, (%s, %s) -> ", callingWriterName, utilityMethod, serializedName, + serializeValue, lambdaWriterName, elementName); + } else { + // But the inner container should be written if it's null. + methodBlock.line("%s.%s(%s, (%s, %s) -> ", callingWriterName, utilityMethod, serializeValue, + lambdaWriterName, elementName); } - if (callToJsonSharedForParentProperties && allPropertiesInGroupFromParent) { - // If all properties in the flattened group are from the parent model, then the call to the parent's - // 'toJsonShared' method will serialize the properties, skip writing serialization logic for this - // method. - return; + methodBlock.indent(() -> { + if (valueSerializationMethod != null) { + if (isJsonMergePatch && containerType instanceof MapType) { + methodBlock.block("", codeBlock -> codeBlock.ifBlock(elementName + "!= null", ifBlock -> { + if (elementType instanceof ClassType && ((ClassType) elementType).isSwaggerType()) { + methodBlock.line("JsonMergePatchHelper.get" + ((ClassType) elementType).getName() + + "Accessor().prepareModelForJsonMergePatch(" + elementName + ", true);"); + } + ifBlock.line(valueSerializationMethod + ";"); + if (elementType instanceof ClassType && ((ClassType) elementType).isSwaggerType()) { + methodBlock.line("JsonMergePatchHelper.get" + ((ClassType) elementType).getName() + + "Accessor().prepareModelForJsonMergePatch(" + elementName + ", false);"); + } + }).elseBlock(elseBlock -> elseBlock.line(lambdaWriterName + ".writeNull();"))); + } else { + methodBlock.line(valueSerializationMethod); + } + } else if (elementType == ClassType.OBJECT) { + methodBlock.line(lambdaWriterName + ".writeUntyped(" + elementName + ")"); + } else if (elementType instanceof IterableType) { + serializeJsonContainerProperty(methodBlock, "writeArray", elementType, + ((IterableType) elementType).getElementType(), serializedName, propertyValueGetter, depth + 1, + isJsonMergePatch); + } else if (elementType instanceof MapType) { + // Assumption is that the key type for the Map is a String. This may not always hold true and when that + // becomes reality this will need to be reworked to handle that case. + serializeJsonContainerProperty(methodBlock, "writeMap", elementType, + ((MapType) elementType).getValueType(), serializedName, propertyValueGetter, depth + 1, + isJsonMergePatch); + } else if (elementType == ClassType.BINARY_DATA) { + methodBlock.line(lambdaWriterName + ".writeUntyped(" + elementName + ")"); + } else { + throw new RuntimeException("Unknown value type " + elementType + " in " + containerType + + " serialization. Need to add support for it."); + } + }); + + if (depth > 0) { + methodBlock.line(")"); + } else { + methodBlock.line(");"); } + } - if (hasPrimitivePropertyInGroup) { - // Simple case where the flattened group has a primitive type where non-null checking doesn't need - // to be done. - methodBlock.line("jsonWriter.writeStartObject(\"" + flattenedProperties.getNodeName() + "\");"); - for (JsonFlattenedPropertiesTree flattened : flattenedProperties.getChildrenNodes().values()) { - handleFlattenedPropertiesSerializationHelper(methodBlock, flattened, isJsonMergePatch, - callToJsonSharedForParentProperties); - } - methodBlock.line("jsonWriter.writeEndObject();"); + /** + * Helper method to serialize flattened properties in a model. + *

+ * Flattened properties are unique as for each level of flattening they'll create a JSON sub-object. But before + * a sub-object is created any field needs to be checked for either being a primitive value or non-null. + * Primitive values are usually serialized no matter their value so those will automatically trigger the JSON + * sub-object to be created, nullable values will be checked for being non-null. + *

+ * In addition to primitive or non-null checking fields, all properties from the same JSON sub-object must be + * written at the same time to prevent an invalid JSON structure. For example if a model has three flattened + * properties with JSON paths "im.flattened", "im.deeper.flattened", and "im.deeper.flattenedtoo" this will + * create the following structure: + * + *

+     * im -> flattened
+     *     | deeper -> flattened
+     *               | flattenedtoo
+     * 
+ * + * So, "im.deeper.flattened" and "im.deeper.flattenedtoo" will need to be serialized at the same time to get the + * correct JSON where there is only one "im: deeper" JSON sub-object. + * + * @param methodBlock The method handling serialization. + */ + private void handleFlattenedPropertiesSerialization(JavaBlock methodBlock, + boolean callToJsonSharedForParentProperties) { + // The initial call to handle flattened properties is using the base node which is just a holder. + for (JsonFlattenedPropertiesTree flattened : propertiesManager.getJsonFlattenedPropertiesTree().getChildrenNodes().values()) { + handleFlattenedPropertiesSerializationHelper(methodBlock, flattened, false, + callToJsonSharedForParentProperties); + } + } + + private static void handleFlattenedPropertiesSerializationHelper(JavaBlock methodBlock, + JsonFlattenedPropertiesTree flattenedProperties, boolean isJsonMergePatch, + boolean callToJsonSharedForParentProperties) { + ClientModelPropertyWithMetadata flattenedProperty = flattenedProperties.getProperty(); + if (flattenedProperty != null) { + // This is a terminal location, only need to add property serialization. + serializeJsonProperty(methodBlock, flattenedProperty.getProperty(), flattenedProperties.getNodeName(), + flattenedProperty.isFromSuperClass(), false, isJsonMergePatch); } else { - // Complex case where all properties in the flattened group are nullable and a check needs to be made - // if any value is non-null. - String condition = propertiesInFlattenedGroup.stream() - .map(property -> (property.isFromSuperClass()) - ? property.getProperty().getGetterName() + "() != null" - : property.getProperty().getName() + " != null") - .collect(Collectors.joining(" || ")); - - methodBlock.ifBlock(condition, ifAction -> { - ifAction.line("jsonWriter.writeStartObject(\"" + flattenedProperties.getNodeName() + "\");"); + // Otherwise this is an intermediate location. + // Check for either any of the properties in this subtree being primitives or add an if block checking that + // any of the properties are non-null. + List propertiesInFlattenedGroup = getClientModelPropertiesInJsonTree( + flattenedProperties); + boolean hasPrimitivePropertyInGroup = false; + boolean allPropertiesInGroupFromParent = true; + for (ClientModelPropertyWithMetadata propertyInFlattenedGroup : propertiesInFlattenedGroup) { + hasPrimitivePropertyInGroup |= propertyInFlattenedGroup.getProperty() + .getWireType() instanceof PrimitiveType; + allPropertiesInGroupFromParent &= propertyInFlattenedGroup.isFromSuperClass(); + } + + if (callToJsonSharedForParentProperties && allPropertiesInGroupFromParent) { + // If all properties in the flattened group are from the parent model, then the call to the parent's + // 'toJsonShared' method will serialize the properties, skip writing serialization logic for this + // method. + return; + } + + if (hasPrimitivePropertyInGroup) { + // Simple case where the flattened group has a primitive type where non-null checking doesn't need + // to be done. + methodBlock.line("jsonWriter.writeStartObject(\"" + flattenedProperties.getNodeName() + "\");"); for (JsonFlattenedPropertiesTree flattened : flattenedProperties.getChildrenNodes().values()) { - handleFlattenedPropertiesSerializationHelper(ifAction, flattened, isJsonMergePatch, + handleFlattenedPropertiesSerializationHelper(methodBlock, flattened, isJsonMergePatch, callToJsonSharedForParentProperties); } - ifAction.line("jsonWriter.writeEndObject();"); - }); + methodBlock.line("jsonWriter.writeEndObject();"); + } else { + // Complex case where all properties in the flattened group are nullable and a check needs to be made + // if any value is non-null. + String condition = propertiesInFlattenedGroup.stream() + .map(property -> (property.isFromSuperClass()) ? property.getProperty().getGetterName() + + "() != null" : property.getProperty().getName() + " != null") + .collect(Collectors.joining(" || ")); + + methodBlock.ifBlock(condition, ifAction -> { + ifAction.line("jsonWriter.writeStartObject(\"" + flattenedProperties.getNodeName() + "\");"); + for (JsonFlattenedPropertiesTree flattened : flattenedProperties.getChildrenNodes().values()) { + handleFlattenedPropertiesSerializationHelper(ifAction, flattened, isJsonMergePatch, + callToJsonSharedForParentProperties); + } + ifAction.line("jsonWriter.writeEndObject();"); + }); + } } } - } - - /* - * Writes the fromJson(JsonReader) implementation. - */ - private void writeFromJson(JavaClass classBlock, ClientModel model, - ClientModelPropertiesManager propertiesManager, JavaSettings settings, - Consumer addGeneratedAnnotation) { - // All classes will create a public fromJson(JsonReader) method that initiates reading. - // How the implementation looks depends on whether the type is a super type, subtype, both, or is a - // stand-alone type. - // - // Intermediate types, those that are both a super type and subtype, will pass null as the type discriminator - // value. This is done as super types are written to only support themselves or their subtypes, passing a - // discriminator into its own super type would confuse this scenario. For example, one of the test Swaggers - // generates the following hierarchy - // - // Fish - // Salmon Shark - // SmartSalmon Sawshark GoblinShark Cookiecuttershark - // - // If Salmon called into Fish with its discriminator and an error occurred it would mention the Shark subtypes - // as potential legal values for deserialization, confusing the Salmon deserializer. So, calling into Salmon - // will only attempt to handle Salmon and SmartSalmon, and same goes for Shark with Shark, Sawshark, - // GoblinShark, and Cookiecuttershark. It's only Fish that will handle all subtypes, as this is the most generic - // super type. This also creates a well-defined bounds for code generation in regard to type hierarchies. - // - // In a real scenario someone deserializing to Salmon should have an exception about discriminator types - // if the JSON payload is a Shark and not get a ClassCastException as it'd be more confusing on why a Shark - // was trying to be converted to a Salmon. - if (isSuperTypeWithDiscriminator(model)) { - writeSuperTypeFromJson(classBlock, model, propertiesManager, settings, addGeneratedAnnotation); - } else { - writeTerminalTypeFromJson(classBlock, propertiesManager, settings, addGeneratedAnnotation); - } - } - - /* - * Writes the readManagementError(JsonReader) implementation for ManagementError subclass. - */ - private void writeManagementErrorDeserializationMethod(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - JavaSettings settings, Consumer addGeneratedAnnotation) { - addGeneratedAnnotation.accept(classBlock); - classBlock.staticMethod( - JavaVisibility.Private, - propertiesManager.getModel().getName() + " " + READ_MANAGEMENT_ERROR_METHOD_NAME + "(JsonReader jsonReader) throws IOException", - methodBlock -> readJsonObjectMethodBody(methodBlock, - deserializationBlock -> writeFromJsonDeserialization0(deserializationBlock, propertiesManager, settings))); - } - /** - * Writes a super type's {@code fromJson(JsonReader)} method. - * - * @param classBlock The class having {@code fromJson(JsonReader)} written to it. - * @param model The Autorest representation of the model. - * @param propertiesManager The properties for the model. - * @param settings The Autorest generation settings. - * @param addGeneratedAnnotation Callback that adds {@code @Generated} annotation to a code block. - */ - private void writeSuperTypeFromJson(JavaClass classBlock, ClientModel model, - ClientModelPropertiesManager propertiesManager, JavaSettings settings, - Consumer addGeneratedAnnotation) { - // Handling polymorphic fields while determining which subclass, or the class itself, to deserialize handles the - // discriminator type always as a String. This is permissible as the found discriminator is never being used in - // a setter or for setting a field, unlike in the actual deserialization method where it needs to be the same - // type as the field. - String fieldNameVariableName = propertiesManager.getJsonReaderFieldNameVariableName(); - ClientModelPropertyWithMetadata discriminatorProperty = propertiesManager.getDiscriminatorProperty(); - readJsonObject(classBlock, propertiesManager, false, methodBlock -> { - // For now, reading polymorphic types will always buffer the current object. - // In the future this can be enhanced to switch if the first property is the discriminator field and to use - // a Map to contain all properties found while searching for the discriminator field. - // TODO (alzimmer): Need to handle non-string wire type discriminator types. - methodBlock.line("String discriminatorValue = null;"); - methodBlock.tryBlock("JsonReader readerToUse = reader.bufferObject()", tryStatement -> { - tryStatement.line("readerToUse.nextToken(); // Prepare for reading"); - tryStatement.line("while (readerToUse.nextToken() != JsonToken.END_OBJECT) {"); - tryStatement.increaseIndent(); - tryStatement.line("String " + fieldNameVariableName + " = readerToUse.getFieldName();"); - tryStatement.line("readerToUse.nextToken();"); - tryStatement.ifBlock( - "\"" + discriminatorProperty.getProperty().getSerializedName() + "\".equals(" + fieldNameVariableName + ")", - ifStatement -> { + /** + * Writes a super type's {@code fromJson(JsonReader)} method. + * + * @param classBlock The class having {@code fromJson(JsonReader)} written to it. + */ + private void writeSuperTypeFromJson(JavaClass classBlock) { + // Handling polymorphic fields while determining which subclass, or the class itself, to deserialize handles the + // discriminator type always as a String. This is permissible as the found discriminator is never being used in + // a setter or for setting a field, unlike in the actual deserialization method where it needs to be the same + // type as the field. + String fieldNameVariableName = propertiesManager.getJsonReaderFieldNameVariableName(); + ClientModelPropertyWithMetadata discriminatorProperty = propertiesManager.getDiscriminatorProperty(); + readJsonObject(classBlock, false, methodBlock -> { + // For now, reading polymorphic types will always buffer the current object. + // In the future this can be enhanced to switch if the first property is the discriminator field and to use + // a Map to contain all properties found while searching for the discriminator field. + // TODO (alzimmer): Need to handle non-string wire type discriminator types. + methodBlock.line("String discriminatorValue = null;"); + methodBlock.tryBlock("JsonReader readerToUse = reader.bufferObject()", tryStatement -> { + tryStatement.line("readerToUse.nextToken(); // Prepare for reading"); + tryStatement.line("while (readerToUse.nextToken() != JsonToken.END_OBJECT) {"); + tryStatement.increaseIndent(); + tryStatement.line("String " + fieldNameVariableName + " = readerToUse.getFieldName();"); + tryStatement.line("readerToUse.nextToken();"); + tryStatement.ifBlock("\"" + discriminatorProperty.getProperty().getSerializedName() + "\".equals(" + + fieldNameVariableName + ")", ifStatement -> { ifStatement.line("discriminatorValue = readerToUse.getString();"); ifStatement.line("break;"); }).elseBlock(elseBlock -> elseBlock.line("readerToUse.skipChildren();")); - tryStatement.decreaseIndent(); - tryStatement.line("}"); + tryStatement.decreaseIndent(); + tryStatement.line("}"); + + tryStatement.line( + "// Use the discriminator value to determine which subtype should be deserialized."); + + // Add deserialization for the super type itself. + JavaIfBlock ifBlock = null; + + // Add deserialization for all child types. + List childTypes = getAllChildTypes(model, new ArrayList<>()); + for (ClientModel childType : childTypes) { + // Determine which serialization method to use based on whether the child type is also a polymorphic + // parent and the child shares the same polymorphic discriminator as this model. + // If the child and parent have different discriminator names then the child will need to be + // deserialized checking the multi-level polymorphic discriminator. + // Using the nested discriminator sample, there is + // Fish : kind + // - Salmon : kind + // - Shark : sharktype + // - Sawshark : sharktype + // So, if deserialization enters Fish and the "kind" is "Shark" then it needs to check the + // "sharktype" to determine if it's a Sawshark or another subtype of Shark. + boolean sameDiscriminator = Objects.equals(childType.getPolymorphicDiscriminatorName(), + model.getPolymorphicDiscriminatorName()); + + if (!sameDiscriminator && !Objects.equals(childType.getParentModelName(), model.getName())) { + // Child model and parent model don't share the same discriminator and the child isn't a direct + // child of the parent model, so skip this child model. This is done as the child model should + // be deserialized by the subtype that defines the different polymorphic discriminator. Using + // the sample above, Fish can't use "kind" to deserialize to a Shark subtype, it needs to use + // "sharktype". + continue; + } - tryStatement.line("// Use the discriminator value to determine which subtype should be deserialized."); + String deserializationMethod = (isSuperTypeWithDiscriminator(childType) && sameDiscriminator) + ? ".fromJsonKnownDiscriminator(readerToUse.reset())" + : ".fromJson(readerToUse.reset())"; - // Add deserialization for the super type itself. - JavaIfBlock ifBlock = null; + ifBlock = ifOrElseIf(tryStatement, ifBlock, + "\"" + childType.getSerializedName() + "\".equals(discriminatorValue)", + ifStatement -> ifStatement.methodReturn(childType.getName() + deserializationMethod)); + } - // Add deserialization for all child types. - List childTypes = getAllChildTypes(model, new ArrayList<>()); - for (ClientModel childType : childTypes) { - // Determine which serialization method to use based on whether the child type is also a polymorphic - // parent and the child shares the same polymorphic discriminator as this model. - // If the child and parent have different discriminator names then the child will need to be - // deserialized checking the multi-level polymorphic discriminator. - // Using the nested discriminator sample, there is - // Fish : kind - // - Salmon : kind - // - Shark : sharktype - // - Sawshark : sharktype - // So, if deserialization enters Fish and the "kind" is "Shark" then it needs to check the - // "sharktype" to determine if it's a Sawshark or another subtype of Shark. - boolean sameDiscriminator = Objects.equals(childType.getPolymorphicDiscriminatorName(), - model.getPolymorphicDiscriminatorName()); - - if (!sameDiscriminator && !Objects.equals(childType.getParentModelName(), model.getName())) { - // Child model and parent model don't share the same discriminator and the child isn't a direct - // child of the parent model, so skip this child model. This is done as the child model should - // be deserialized by the subtype that defines the different polymorphic discriminator. Using - // the sample above, Fish can't use "kind" to deserialize to a Shark subtype, it needs to use - // "sharktype". - continue; + if (ifBlock == null) { + tryStatement.methodReturn("fromJsonKnownDiscriminator(readerToUse.reset())"); + } else { + ifBlock.elseBlock( + elseBlock -> elseBlock.methodReturn("fromJsonKnownDiscriminator(readerToUse.reset())")); } + }); + }); - String deserializationMethod = (isSuperTypeWithDiscriminator(childType) && sameDiscriminator) - ? ".fromJsonKnownDiscriminator(readerToUse.reset())" - : ".fromJson(readerToUse.reset())"; + readJsonObject(classBlock, true, this::writeFromJsonDeserialization); + } - ifBlock = ifOrElseIf(tryStatement, ifBlock, - "\"" + childType.getSerializedName() + "\".equals(discriminatorValue)", - ifStatement -> ifStatement.methodReturn(childType.getName() + deserializationMethod)); + private static List getAllChildTypes(ClientModel model, List childTypes) { + for (ClientModel childType : model.getDerivedModels()) { + childTypes.add(childType); + if (!CoreUtils.isNullOrEmpty(childType.getDerivedModels())) { + getAllChildTypes(childType, childTypes); } + } - if (ifBlock == null) { - tryStatement.methodReturn("fromJsonKnownDiscriminator(readerToUse.reset())"); + return childTypes; + } + + /** + * Gets the additionalProperty model property from this model or its superclass. + * + * @return the additionalProperty model property from this model or its superclass. + */ + private ClientModelProperty getAdditionalPropertiesPropertyInModelOrFromSuper() { + return propertiesManager.getAdditionalProperties() != null + ? propertiesManager.getAdditionalProperties() + : propertiesManager.getSuperAdditionalPropertiesProperty(); + } + + private void writeFromJsonDeserialization(JavaBlock methodBlock) { + // Add the deserialization logic. + methodBlock.indent(() -> { + if (isManagementErrorSubclass.test(model)) { + methodBlock.line("JsonReader bufferedReader = reader.bufferObject();"); + methodBlock.line("bufferedReader.nextToken();"); + + addReaderWhileLoop("bufferedReader", methodBlock, true, false, whileBlock -> methodBlock.ifBlock( + "\"error\".equals(" + propertiesManager.getJsonReaderFieldNameVariableName() + ")", + ifAction -> ifAction.line("return " + READ_MANAGEMENT_ERROR_METHOD_NAME + "(bufferedReader);")) + .elseBlock(elseAction -> elseAction.line("bufferedReader.skipChildren();"))); + + methodBlock.methodReturn(READ_MANAGEMENT_ERROR_METHOD_NAME + "(bufferedReader.reset())"); } else { - ifBlock.elseBlock( - elseBlock -> elseBlock.methodReturn("fromJsonKnownDiscriminator(readerToUse.reset())")); + writeFromJsonDeserialization0(methodBlock); } }); - }, addGeneratedAnnotation, settings); + } - readJsonObject(classBlock, propertiesManager, true, - methodBlock -> writeFromJsonDeserialization(methodBlock, propertiesManager, settings), - addGeneratedAnnotation, settings); - } + private void writeFromJsonDeserialization0(JavaBlock methodBlock) { + // Initialize local variables to track what has been deserialized. + initializeLocalVariables(methodBlock, false); + String fieldNameVariableName = propertiesManager.getJsonReaderFieldNameVariableName(); - private static List getAllChildTypes(ClientModel model, List childTypes) { - for (ClientModel childType : model.getDerivedModels()) { - childTypes.add(childType); - if (!CoreUtils.isNullOrEmpty(childType.getDerivedModels())) { - getAllChildTypes(childType, childTypes); - } - } + // Add the outermost while loop to read the JSON object. + addReaderWhileLoop(methodBlock, true, false, whileBlock -> { + if (useFromJsonShared && model.isPolymorphicParent()) { + // If we can use 'fromJsonShared' and this model is a super type, then we can use a customized + // 'fromJson' / 'fromJsonKnownDiscriminator' method to handle deserialization. + // This will generate the following logic: + // + // if (!fromJsonShared(reader, fieldName, deserializedModel)) { + // handleUnknownProperty + // } + String ifBlockCondition = "!" + model.getName() + ".fromJsonShared(reader, " + + fieldNameVariableName + ", " + propertiesManager.getDeserializedModelName() + ")"; + methodBlock.ifBlock(ifBlockCondition, ifBlock -> generateUnknownFieldLogic(ifBlock, null)); + return; + } - return childTypes; - } + // Loop over all properties and generate their deserialization handling. + AtomicReference ifBlockReference = new AtomicReference<>(null); - /** - * Gets the additionalProperty model property from this model or its superclass. - * - * @param propertiesManager The properties for the model. - * @return the additionalProperty model property from this model or its superclass. - */ - private static ClientModelProperty getAdditionalPropertiesPropertyInModelOrFromSuper( - ClientModelPropertiesManager propertiesManager) { - return propertiesManager.getAdditionalProperties() != null - ? propertiesManager.getAdditionalProperties() - : propertiesManager.getSuperAdditionalPropertiesProperty(); - } - - /** - * Writes a terminal type's {@code fromJson(JsonReader)} method. - *

- * A terminal type is either a type without polymorphism or is the terminal type in a polymorphic hierarchy. - * - * @param classBlock The class having {@code fromJson(JsonReader)} written to it. - * @param propertiesManager The properties for the model. - * @param settings The Autorest generation settings. - * @param addGeneratedAnnotation Callback that adds {@code @Generated} annotation to a code block. - */ - private void writeTerminalTypeFromJson(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - JavaSettings settings, Consumer addGeneratedAnnotation) { - readJsonObject(classBlock, propertiesManager, false, - methodBlock -> writeFromJsonDeserialization(methodBlock, propertiesManager, settings), - addGeneratedAnnotation, settings); - } + BiConsumer consumer + = (property, fromSuper) -> handleJsonPropertyDeserialization(property, whileBlock, ifBlockReference, + fromSuper, false); - private void writeFromJsonDeserialization(JavaBlock methodBlock, - ClientModelPropertiesManager propertiesManager, JavaSettings settings) { - // Add the deserialization logic. - methodBlock.indent(() -> { - if (isManagementErrorSubclass(propertiesManager.getModel(), settings)) { - writeManagementErrorAdaption(methodBlock, propertiesManager); - } else { - writeFromJsonDeserialization0(methodBlock, propertiesManager, settings); - } - }); - } + Map modelPropertyMap = new HashMap<>(); + for (ClientModelProperty parentProperty : ClientModelUtil.getParentProperties(model)) { + modelPropertyMap.put(parentProperty.getName(), parentProperty); + } + for (ClientModelProperty property : model.getProperties()) { + modelPropertyMap.put(property.getName(), property); + } - private static void writeManagementErrorAdaption(JavaBlock methodBlock, ClientModelPropertiesManager propertiesManager) { - methodBlock.line("JsonReader bufferedReader = reader.bufferObject();"); - methodBlock.line("bufferedReader.nextToken();"); + if (useFromJsonShared) { + // If this model is a subtype, and 'fromJsonShared' can be used, instead of generating the + // deserialization of the parent model(s) in 'fromJson' call to the parent class's 'fromJsonShared'. + String ifBlockCondition = model.getParentModelName() + ".fromJsonShared(reader, " + + fieldNameVariableName + ", " + propertiesManager.getDeserializedModelName() + ")"; + ifBlockReference.set(methodBlock.ifBlock(ifBlockCondition, ifBlock -> ifBlock.line("continue;"))); + } else { + // Child classes may contain properties that shadow parents' ones. + // Thus, we only take the shadowing ones, not the ones shadowed. + Map superRequiredToDeserialized = new LinkedHashMap<>(); + propertiesManager.forEachSuperRequiredProperty(property -> { + if (!property.isConstant() && modelPropertyMap.get(property.getName()) == property) { + superRequiredToDeserialized.put(property.getName(), property); + } + }); + superRequiredToDeserialized.values().forEach(property -> consumer.accept(property, true)); + + // Child classes may contain properties that shadow parents' ones. + // Thus, we only take the shadowing ones, not the ones shadowed. + Map superSettersToDeserialized = new LinkedHashMap<>(); + propertiesManager.forEachSuperSetterProperty(property -> { + if (!property.isConstant() && modelPropertyMap.get(property.getName()) == property) { + superSettersToDeserialized.put(property.getName(), property); + } + }); + superSettersToDeserialized.values().forEach(property -> consumer.accept(property, true)); + } - String fieldNameVariableName = propertiesManager.getJsonReaderFieldNameVariableName(); - addReaderWhileLoop("bufferedReader", methodBlock, true, fieldNameVariableName, false, whileBlock -> - methodBlock.ifBlock("\"error\".equals(" + fieldNameVariableName + ")", - ifAction -> ifAction.line("return " + READ_MANAGEMENT_ERROR_METHOD_NAME + "(bufferedReader);")) - .elseBlock(elseAction -> elseAction.line("bufferedReader.skipChildren();"))); + generateThisFromJson(ifBlockReference, consumer, methodBlock, false, useFromJsonShared); - methodBlock.methodReturn(READ_MANAGEMENT_ERROR_METHOD_NAME + "(bufferedReader.reset())"); - } + // All properties have been checked for, add an else block that will either ignore unknown properties + // or add them into an additional properties bag. + generateUnknownFieldLogic(whileBlock, ifBlockReference.get()); + }); - private static void writeFromJsonDeserialization0(JavaBlock methodBlock, - ClientModelPropertiesManager propertiesManager, JavaSettings settings) { - // Initialize local variables to track what has been deserialized. - initializeLocalVariables(methodBlock, propertiesManager, false, settings); + // Add the validation and return logic. + handleReadReturn(methodBlock); + } - boolean polymorphicJsonMergePatchScenario = propertiesManager.getModel().isPolymorphic() - && ClientModelUtil.isJsonMergePatchModel(propertiesManager.getModel(), settings); - boolean useFromJsonShared = canUseFromJsonShared(propertiesManager); + private void generateThisFromJson(AtomicReference ifBlockReference, + BiConsumer consumer, JavaBlock methodBlock, boolean isFromJsonShared, + boolean usingFromJsonShared) { + propertiesManager.forEachRequiredProperty(property -> { + if (property.isConstant()) { + return; + } - String fieldNameVariableName = propertiesManager.getJsonReaderFieldNameVariableName(); + if (skipDeserializingParentDefinedDiscriminator(usingFromJsonShared, isFromJsonShared, property)) { + return; + } - // Add the outermost while loop to read the JSON object. - addReaderWhileLoop(methodBlock, true, fieldNameVariableName, false, whileBlock -> { - if (useFromJsonShared && propertiesManager.getModel().isPolymorphicParent()) { - // If we can use 'fromJsonShared' and this model is a super type, then we can use a customized - // 'fromJson' / 'fromJsonKnownDiscriminator' method to handle deserialization. - // This will generate the following logic: - // - // if (!fromJsonShared(reader, fieldName, deserializedModel)) { - // handleUnknownProperty - // } - String ifBlockCondition = "!" + propertiesManager.getModel().getName() + ".fromJsonShared(reader, " - + fieldNameVariableName + ", " + propertiesManager.getDeserializedModelName() + ")"; - methodBlock.ifBlock(ifBlockCondition, - ifBlock -> generateUnknownFieldLogic(ifBlock, null, propertiesManager)); - return; - } + consumer.accept(property, false); + }); + propertiesManager.forEachSetterProperty(property -> { + if (skipDeserializingParentDefinedDiscriminator(usingFromJsonShared, isFromJsonShared, property)) { + return; + } - // Loop over all properties and generate their deserialization handling. - AtomicReference ifBlockReference = new AtomicReference<>(null); + consumer.accept(property, false); + }); - BiConsumer consumer = (property, fromSuper) -> - handleJsonPropertyDeserialization(propertiesManager.getModel(), property, - propertiesManager.getDeserializedModelName(), whileBlock, ifBlockReference, - fieldNameVariableName, fromSuper, propertiesManager.hasConstructorArguments(), settings, - polymorphicJsonMergePatchScenario, false); + JavaIfBlock ifBlock = ifBlockReference.get(); - Map modelPropertyMap = new HashMap<>(); - for (ClientModelProperty parentProperty : ClientModelUtil.getParentProperties(propertiesManager.getModel())) { - modelPropertyMap.put(parentProperty.getName(), parentProperty); - } - for (ClientModelProperty property : propertiesManager.getModel().getProperties()) { - modelPropertyMap.put(property.getName(), property); + // Add flattened properties if we aren't using 'fromJsonShared' or some of the flattened properties are defined + // by this model. + if (!usingFromJsonShared || !propertiesManager.isAllFlattenedPropertiesFromParent()) { + handleFlattenedPropertiesDeserialization(methodBlock, ifBlock, isFromJsonShared); } + } - if (useFromJsonShared) { - // If this model is a subtype, and 'fromJsonShared' can be used, instead of generating the - // deserialization of the parent model(s) in 'fromJson' call to the parent class's 'fromJsonShared'. - String ifBlockCondition = propertiesManager.getModel().getParentModelName() + ".fromJsonShared(reader, " - + fieldNameVariableName + ", " + propertiesManager.getDeserializedModelName() + ")"; - ifBlockReference.set(methodBlock.ifBlock(ifBlockCondition, ifBlock -> ifBlock.line("continue;"))); - } else { - // Child classes may contain properties that shadow parents' ones. - // Thus, we only take the shadowing ones, not the ones shadowed. - Map superRequiredToDeserialized = new LinkedHashMap<>(); - propertiesManager.forEachSuperRequiredProperty(property -> { - if (!property.isConstant() && modelPropertyMap.get(property.getName()) == property) { - superRequiredToDeserialized.put(property.getName(), property); - } - }); - superRequiredToDeserialized.values().forEach(property -> consumer.accept(property, true)); - - // Child classes may contain properties that shadow parents' ones. - // Thus, we only take the shadowing ones, not the ones shadowed. - Map superSettersToDeserialized = new LinkedHashMap<>(); - propertiesManager.forEachSuperSetterProperty(property -> { - if (!property.isConstant() && modelPropertyMap.get(property.getName()) == property) { - superSettersToDeserialized.put(property.getName(), property); + private boolean skipDeserializingParentDefinedDiscriminator(boolean usingFromJsonShared, + boolean isFromJsonShared, ClientModelProperty property) { + // If this type is using 'fromJsonShared' from the parent model, skip deserializing polymorphic + // discriminators if it is defined by a parent model. + return (usingFromJsonShared || (isFromJsonShared && !CoreUtils.isNullOrEmpty(model.getParentModelName()))) + && property.isPolymorphicDiscriminator() && !model.isPolymorphicDiscriminatorDefinedByModel(); + } + + private void generateUnknownFieldLogic(JavaBlock whileBlock, JavaIfBlock ifBlock) { + ClientModelProperty additionalProperty = getAdditionalPropertiesPropertyInModelOrFromSuper(); + handleUnknownJsonFieldDeserialization(whileBlock, ifBlock, additionalProperty); + } + + /** + * Adds a static method to the class with the signature that handles reading the JSON string into the object + * type. + *

+ * If {@code superTypeReading} is true the method will be package-private and named + * {@code fromJsonWithKnownDiscriminator} instead of being public and named {@code fromJson}. This is done as + * super types use their {@code fromJson} method to determine the discriminator value and pass the reader to the + * specific type being deserialized. The specific type being deserialized may be the super type itself, so it + * cannot pass to {@code fromJson} as this will be a circular call and if the specific type being deserialized + * is an intermediate type (a type having both super and subclasses) it will attempt to perform discriminator + * validation which has already been done. + * + * @param classBlock The class where the {@code fromJson} method is being written. + * @param superTypeReading Whether the object reading is for a super type. + * @param deserializationBlock Logic for deserializing the object. + */ + private void readJsonObject(JavaClass classBlock, boolean superTypeReading, + Consumer deserializationBlock) { + JavaVisibility visibility = superTypeReading ? JavaVisibility.PackagePrivate : JavaVisibility.Public; + String methodName = superTypeReading ? "fromJsonKnownDiscriminator" : "fromJson"; + + String modelName = model.getName(); + boolean hasRequiredProperties = propertiesManager.hasRequiredProperties(); + + if (!superTypeReading) { + classBlock.javadocComment(javadocComment -> { + javadocComment.description("Reads an instance of " + modelName + " from the JsonReader."); + javadocComment.param("jsonReader", "The JsonReader being read."); + javadocComment.methodReturns( + "An instance of " + modelName + " if the JsonReader was pointing to an " + + "instance of it, or null if it was pointing to JSON null."); + + if (hasRequiredProperties) { + javadocComment.methodThrows("IllegalStateException", + "If the deserialized JSON object was missing any required properties."); } + + javadocComment.methodThrows("IOException", + "If an error occurs while reading the " + modelName + "."); }); - superSettersToDeserialized.values().forEach(property -> consumer.accept(property, true)); } - generateThisFromJson(propertiesManager, ifBlockReference, consumer, methodBlock, settings, - polymorphicJsonMergePatchScenario, false, useFromJsonShared); - - // All properties have been checked for, add an else block that will either ignore unknown properties - // or add them into an additional properties bag. - generateUnknownFieldLogic(whileBlock, ifBlockReference.get(), propertiesManager); - }); - - // Add the validation and return logic. - handleReadReturn(methodBlock, propertiesManager.getModel().getName(), propertiesManager, settings); - } - - private static void generateThisFromJson(ClientModelPropertiesManager propertiesManager, - AtomicReference ifBlockReference, BiConsumer consumer, - JavaBlock methodBlock, JavaSettings settings, boolean polymorphicJsonMergePatchScenario, - boolean isFromJsonShared, boolean usingFromJsonShared) { - propertiesManager.forEachRequiredProperty(property -> { - if (property.isConstant()) { - return; - } + addGeneratedAnnotation.accept(classBlock); + classBlock.staticMethod(visibility, + modelName + " " + methodName + "(JsonReader jsonReader) throws IOException", + methodBlock -> readJsonObjectMethodBody(methodBlock, deserializationBlock)); + + if (superTypeReading && useFromJsonShared && model.isPolymorphicParent()) { + // Add a package-private 'fromJsonShared' method that can handle deserializing properties defined in the parent + // class. + String fieldName = propertiesManager.getJsonReaderFieldNameVariableName(); + String modelDeserializedName = propertiesManager.getDeserializedModelName(); + String methodDefinition = "boolean fromJsonShared(JsonReader reader, String " + fieldName + ", " + + modelName + " " + modelDeserializedName + ") throws IOException"; + addGeneratedAnnotation.accept(classBlock); + classBlock.staticMethod(JavaVisibility.PackagePrivate, methodDefinition, methodBlock -> { + AtomicReference ifBlockReference = new AtomicReference<>(); + if (!CoreUtils.isNullOrEmpty(model.getParentModelName())) { + String callToSuperFromJsonShared = model.getParentModelName() + ".fromJsonShared(reader, " + + propertiesManager.getJsonReaderFieldNameVariableName() + ", " + + propertiesManager.getDeserializedModelName() + ")"; + ifBlockReference.set( + methodBlock.ifBlock(callToSuperFromJsonShared, ifBlock -> ifBlock.methodReturn("true"))); + } - if (skipDeserializingParentDefinedDiscriminator(usingFromJsonShared, isFromJsonShared, propertiesManager, - property)) { - return; - } + BiConsumer consumer + = (property, fromSuper) -> handleJsonPropertyDeserialization(property, methodBlock, + ifBlockReference, fromSuper, true); + generateThisFromJson(ifBlockReference, consumer, methodBlock, true, false); - consumer.accept(property, false); - }); - propertiesManager.forEachSetterProperty(property -> { - if (skipDeserializingParentDefinedDiscriminator(usingFromJsonShared, isFromJsonShared, propertiesManager, - property)) { - return; + methodBlock.methodReturn("false"); + }); } - - consumer.accept(property, false); - }); - - JavaIfBlock ifBlock = ifBlockReference.get(); - - // Add flattened properties if we aren't using 'fromJsonShared' or some of the flattened properties are defined - // by this model. - if (!usingFromJsonShared || !propertiesManager.isAllFlattenedPropertiesFromParent()) { - handleFlattenedPropertiesDeserialization(propertiesManager.getJsonFlattenedPropertiesTree(), methodBlock, - ifBlock, propertiesManager.getAdditionalProperties(), - propertiesManager.getJsonReaderFieldNameVariableName(), propertiesManager.hasConstructorArguments(), - settings, polymorphicJsonMergePatchScenario, isFromJsonShared); } - } - - private static boolean skipDeserializingParentDefinedDiscriminator(boolean usingFromJsonShared, - boolean isFromJsonShared, ClientModelPropertiesManager propertiesManager, ClientModelProperty property) { - // If this type is using 'fromJsonShared' from the parent model, skip deserializing polymorphic - // discriminators if it is defined by a parent model. - return (usingFromJsonShared - || (isFromJsonShared && !CoreUtils.isNullOrEmpty(propertiesManager.getModel().getParentModelName()))) - && property.isPolymorphicDiscriminator() - && !propertiesManager.getModel().isPolymorphicDiscriminatorDefinedByModel(); - } - private static void generateUnknownFieldLogic(JavaBlock whileBlock, JavaIfBlock ifBlock, - ClientModelPropertiesManager propertiesManager) { - ClientModelProperty additionalProperty = getAdditionalPropertiesPropertyInModelOrFromSuper(propertiesManager); - handleUnknownJsonFieldDeserialization(whileBlock, ifBlock, additionalProperty, - propertiesManager.getJsonReaderFieldNameVariableName()); - } - - /** - * Whether the given model is subclass of ManagementError, which needs special deserialization adaption. - * @param model the model to check - * @param settings JavaSettings instance - * @return whether the given model is subclass of ManagementError - */ - protected boolean isManagementErrorSubclass(ClientModel model, JavaSettings settings) { - return false; - } - - /** - * Adds a static method to the class with the signature that handles reading the JSON string into the object type. - *

- * If {@code superTypeReading} is true the method will be package-private and named - * {@code fromJsonWithKnownDiscriminator} instead of being public and named {@code fromJson}. This is done as super - * types use their {@code fromJson} method to determine the discriminator value and pass the reader to the specific - * type being deserialized. The specific type being deserialized may be the super type itself, so it cannot pass to - * {@code fromJson} as this will be a circular call and if the specific type being deserialized is an intermediate - * type (a type having both super and subclasses) it will attempt to perform discriminator validation which has - * already been done. - * - * @param classBlock The class where the {@code fromJson} method is being written. - * @param propertiesManager Properties information about the object being deserialized. - * @param superTypeReading Whether the object reading is for a super type. - * @param deserializationBlock Logic for deserializing the object. - * @param addGeneratedAnnotation Callback that adds {@code @Generated} annotation to a code block. - */ - private static void readJsonObject(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - boolean superTypeReading, Consumer deserializationBlock, - Consumer addGeneratedAnnotation, JavaSettings settings) { - JavaVisibility visibility = superTypeReading ? JavaVisibility.PackagePrivate : JavaVisibility.Public; - String methodName = superTypeReading ? "fromJsonKnownDiscriminator" : "fromJson"; - - String modelName = propertiesManager.getModel().getName(); - boolean hasRequiredProperties = propertiesManager.hasRequiredProperties(); - - if (!superTypeReading) { - classBlock.javadocComment(javadocComment -> { - javadocComment.description("Reads an instance of " + modelName + " from the JsonReader."); - javadocComment.param("jsonReader", "The JsonReader being read."); - javadocComment.methodReturns("An instance of " + modelName + " if the JsonReader was pointing to an " - + "instance of it, or null if it was pointing to JSON null."); - - if (hasRequiredProperties) { - javadocComment.methodThrows("IllegalStateException", - "If the deserialized JSON object was missing any required properties."); - } - - javadocComment.methodThrows("IOException", "If an error occurs while reading the " + modelName + "."); - }); + private static void readJsonObjectMethodBody(JavaBlock methodBlock, Consumer deserializationBlock) { + // For now, use the basic readObject which will return null if the JsonReader is pointing to JsonToken.NULL. + // + // Support for a default value if null will need to be supported and for objects that get their value + // from a JSON value instead of JSON object or are an array type. + methodBlock.line("return jsonReader.readObject(reader -> {"); + deserializationBlock.accept(methodBlock); + methodBlock.line("});"); } - addGeneratedAnnotation.accept(classBlock); - classBlock.staticMethod(visibility, modelName + " " + methodName + "(JsonReader jsonReader) throws IOException", - methodBlock -> readJsonObjectMethodBody(methodBlock, deserializationBlock)); - - if (superTypeReading - && canUseFromJsonShared(propertiesManager) - && propertiesManager.getModel().isPolymorphicParent()) { - // Add a package-private 'fromJsonShared' method that can handle deserializing properties defined in the parent - // class. - String fieldName = propertiesManager.getJsonReaderFieldNameVariableName(); - String modelDeserializedName = propertiesManager.getDeserializedModelName(); - String methodDefinition = "boolean fromJsonShared(JsonReader reader, String " + fieldName + ", " - + modelName + " " + modelDeserializedName + ") throws IOException"; - addGeneratedAnnotation.accept(classBlock); - classBlock.staticMethod(JavaVisibility.PackagePrivate, methodDefinition, methodBlock -> { - boolean polymorphicJsonMergePatchScenario = propertiesManager.getModel().isPolymorphic() - && ClientModelUtil.isJsonMergePatchModel(propertiesManager.getModel(), settings); - - AtomicReference ifBlockReference = new AtomicReference<>(); - if (!CoreUtils.isNullOrEmpty(propertiesManager.getModel().getParentModelName())) { - String callToSuperFromJsonShared = propertiesManager.getModel().getParentModelName() - + ".fromJsonShared(reader, " + propertiesManager.getJsonReaderFieldNameVariableName() + ", " - + propertiesManager.getDeserializedModelName() + ")"; - ifBlockReference.set(methodBlock.ifBlock(callToSuperFromJsonShared, - ifBlock -> ifBlock.methodReturn("true"))); + /** + * Initializes the local variables needed to maintain what has been deserialized. + * + * @param methodBlock The method handling deserialization. + */ + private void initializeLocalVariables(JavaBlock methodBlock, boolean isXml) { + if (propertiesManager.hasConstructorArguments()) { + if (isXml) { + // XML only needs to initialize the XML element properties. XML attribute properties are initialized with + // their XML value. + propertiesManager.forEachSuperXmlElement( + element -> initializeLocalVariable(methodBlock, element, true)); + propertiesManager.forEachXmlElement( + element -> initializeLocalVariable(methodBlock, element, false)); + } else { + propertiesManager.forEachSuperRequiredProperty(property -> { + if (property.isConstant()) { + // Constants are never deserialized. + return; + } + initializeLocalVariable(methodBlock, property, true); + }); + propertiesManager.forEachSuperSetterProperty(property -> { + if (ClientModelUtil.readOnlyNotInCtor(model, property, settings)) { + initializeShadowPropertyLocalVariable(methodBlock, property); + } else { + initializeLocalVariable(methodBlock, property, true); + } + }); + propertiesManager.forEachRequiredProperty(property -> { + if (property.isConstant()) { + // Constants are never deserialized. + return; + } + initializeLocalVariable(methodBlock, property, false); + }); + propertiesManager.forEachSetterProperty( + property -> initializeLocalVariable(methodBlock, property, false)); } - - BiConsumer consumer - = (property, fromSuper) -> handleJsonPropertyDeserialization(propertiesManager.getModel(), property, - modelDeserializedName, methodBlock, ifBlockReference, fieldName, fromSuper, - propertiesManager.hasConstructorArguments(), settings, polymorphicJsonMergePatchScenario, true); - generateThisFromJson(propertiesManager, ifBlockReference, consumer, methodBlock, settings, - polymorphicJsonMergePatchScenario, true, false); - - methodBlock.methodReturn("false"); - }); - } - } - - private static void readJsonObjectMethodBody(JavaBlock methodBlock, Consumer deserializationBlock) { - // For now, use the basic readObject which will return null if the JsonReader is pointing to JsonToken.NULL. - // - // Support for a default value if null will need to be supported and for objects that get their value - // from a JSON value instead of JSON object or are an array type. - methodBlock.line("return jsonReader.readObject(reader -> {"); - deserializationBlock.accept(methodBlock); - methodBlock.line("});"); - } - - private static boolean canUseFromJsonShared(ClientModelPropertiesManager propertiesManager) { - // If the model is part of a polymorphic hierarchy and all models in the polymorphic hierarchy are in the same - // package we can generate a package-private 'toJsonShared' method that can handle deserializing properties - // defined in the parent class(es). - // This will prevent duplicating the deserialization logic for parent properties in each subclass. - return !propertiesManager.hasConstructorArguments() - && propertiesManager.getModel().isAllPolymorphicModelsInSamePackage(); - } - - /** - * Initializes the local variables needed to maintain what has been deserialized. - * - * @param methodBlock The method handling deserialization. - * @param propertiesManager The property manager for the model. - */ - private static void initializeLocalVariables(JavaBlock methodBlock, ClientModelPropertiesManager propertiesManager, - boolean isXml, JavaSettings settings) { - if (propertiesManager.hasConstructorArguments()) { - if (isXml) { - // XML only needs to initialize the XML element properties. XML attribute properties are initialized with - // their XML value. - propertiesManager.forEachSuperXmlElement(element -> initializeLocalVariable(methodBlock, element, true, - settings)); - propertiesManager.forEachXmlElement(element -> initializeLocalVariable(methodBlock, element, false, - settings)); } else { - propertiesManager.forEachSuperRequiredProperty(property -> { - if (property.isConstant()) { - // Constants are never deserialized. - return; - } - initializeLocalVariable(methodBlock, property, true, settings); - }); - propertiesManager.forEachSuperSetterProperty(property -> { - if (ClientModelUtil.readOnlyNotInCtor(propertiesManager.getModel(), property, settings)) { - initializeShadowPropertyLocalVariable(methodBlock, property); - } else { - initializeLocalVariable(methodBlock, property, true, settings); - } - }); - propertiesManager.forEachRequiredProperty(property -> { - if (property.isConstant()) { - // Constants are never deserialized. - return; - } - initializeLocalVariable(methodBlock, property, false, settings); - }); - propertiesManager.forEachSetterProperty(property -> initializeLocalVariable(methodBlock, property, - false, settings)); + methodBlock.line(model.getName() + " " + propertiesManager.getDeserializedModelName() + " = new " + + model.getName() + "();"); } - } else { - String modelName = propertiesManager.getModel().getName(); - methodBlock.line(modelName + " " + propertiesManager.getDeserializedModelName() + " = new " - + modelName + "();"); - } - ClientModelProperty additionalProperty = getAdditionalPropertiesPropertyInModelOrFromSuper(propertiesManager); - if (additionalProperty != null) { - initializeLocalVariable(methodBlock, additionalProperty, false, settings); + ClientModelProperty additionalProperty = getAdditionalPropertiesPropertyInModelOrFromSuper(); + if (additionalProperty != null) { + initializeLocalVariable(methodBlock, additionalProperty, false); + } } - } - - /* - * Shadow properties from parent should be initialized as wired type. - */ - private static void initializeShadowPropertyLocalVariable(JavaBlock methodBlock, ClientModelProperty property) { - IType type = property.getWireType(); - String defaultValue = property.isPolymorphicDiscriminator() - ? property.getDefaultValue() : type.defaultValueExpression(); - methodBlock.line(type + " " + property.getName() + " = " + defaultValue + ";"); - } - private static void initializeLocalVariable(JavaBlock methodBlock, ClientModelProperty property, boolean fromSuper, - JavaSettings settings) { - if (includePropertyInConstructor(property, settings) && !settings.isDisableRequiredJsonAnnotation()) { - // Required properties need an additional boolean variable to indicate they've been found. - methodBlock.line("boolean " + property.getName() + "Found = false;"); - } - - // Always instantiate the local variable. - // If the property is part of the constructor or set by a setter method from the super class, initialize the - // local variable with the client type. Otherwise, initialize as the wire type to prevent multiple conversions - // between wire and client types. - IType type = (includePropertyInConstructor(property, settings) || fromSuper) - ? property.getClientType() : property.getWireType(); - String defaultValue = property.isPolymorphicDiscriminator() - ? property.getDefaultValue() : type.defaultValueExpression(); - methodBlock.line(type + " " + property.getName() + " = " + defaultValue + ";"); - } - - /** - * Adds the while loop that handles reading the JSON object until it is fully consumed. - * - * @param methodBlock The method handling deserialization. - * @param initializeFieldNameVariable Whether the {@code fieldNameVariableName} variable needs to be initialized. If - * this is a nested while loop the variable doesn't need to be initialized. - * @param fieldNameVariableName The name for the variable that tracks the JSON field name. - * @param isXml Whether the reader while loop is for XML reading. - * @param whileBlock The consumer that adds deserialization logic into the while loop. - */ - private static void addReaderWhileLoop(JavaBlock methodBlock, boolean initializeFieldNameVariable, - String fieldNameVariableName, boolean isXml, Consumer whileBlock) { - addReaderWhileLoop("reader", methodBlock, initializeFieldNameVariable, fieldNameVariableName, - isXml, whileBlock); - } + /* + * Shadow properties from parent should be initialized as wired type. + */ + private static void initializeShadowPropertyLocalVariable(JavaBlock methodBlock, ClientModelProperty property) { + IType type = property.getWireType(); + String defaultValue = property.isPolymorphicDiscriminator() + ? property.getDefaultValue() + : type.defaultValueExpression(); + methodBlock.line(type + " " + property.getName() + " = " + defaultValue + ";"); + } - /** - * Adds the while loop that handles reading the JSON object until it is fully consumed. - * - * @param readerVariableName The name of the local reader variable. - * @param methodBlock The method handling deserialization. - * @param initializeFieldNameVariable Whether the {@code fieldNameVariableName} variable needs to be initialized. If - * this is a nested while loop the variable doesn't need to be initialized. - * @param fieldNameVariableName The name for the variable that tracks the JSON field name. - * @param isXml Whether the reader while loop is for XML reading. - * @param whileBlock The consumer that adds deserialization logic into the while loop. - */ - private static void addReaderWhileLoop(String readerVariableName, JavaBlock methodBlock, boolean initializeFieldNameVariable, - String fieldNameVariableName, boolean isXml, Consumer whileBlock) { - String whileCheck = isXml - ? readerVariableName + ".nextElement() != XmlToken.END_ELEMENT" - : readerVariableName + ".nextToken() != JsonToken.END_OBJECT"; - - methodBlock.block("while (" + whileCheck + ")", whileAction -> { - String fieldNameInitialization = ""; - if (initializeFieldNameVariable) { - fieldNameInitialization = isXml ? "QName" : "String"; + private void initializeLocalVariable(JavaBlock methodBlock, ClientModelProperty property, boolean fromSuper) { + if (includePropertyInConstructor(property, settings) && !settings.isDisableRequiredJsonAnnotation()) { + // Required properties need an additional boolean variable to indicate they've been found. + methodBlock.line("boolean " + property.getName() + "Found = false;"); } - methodBlock.line("%s %s = %s.get%sName();", fieldNameInitialization, fieldNameVariableName, - readerVariableName, isXml ? "Element" : "Field"); + // Always instantiate the local variable. + // If the property is part of the constructor or set by a setter method from the super class, initialize the + // local variable with the client type. Otherwise, initialize as the wire type to prevent multiple conversions + // between wire and client types. + IType type = (includePropertyInConstructor(property, settings) || fromSuper) + ? property.getClientType() + : property.getWireType(); + String defaultValue = property.isPolymorphicDiscriminator() + ? property.getDefaultValue() + : type.defaultValueExpression(); + methodBlock.line(type + " " + property.getName() + " = " + defaultValue + ";"); + } + + /** + * Adds the while loop that handles reading the JSON object until it is fully consumed. + * + * @param methodBlock The method handling deserialization. + * @param initializeFieldNameVariable Whether the {@code fieldNameVariableName} variable needs to be + * initialized. If this is a nested while loop the variable doesn't need to be initialized. + * @param isXml Whether the reader while loop is for XML reading. + * @param whileBlock The consumer that adds deserialization logic into the while loop. + */ + private void addReaderWhileLoop(JavaBlock methodBlock, boolean initializeFieldNameVariable, boolean isXml, + Consumer whileBlock) { + addReaderWhileLoop("reader", methodBlock, initializeFieldNameVariable, isXml, whileBlock); + } + + /** + * Adds the while loop that handles reading the JSON object until it is fully consumed. + * + * @param readerVariableName The name of the local reader variable. + * @param methodBlock The method handling deserialization. + * @param initializeFieldNameVariable Whether the {@code fieldNameVariableName} variable needs to be + * initialized. If this is a nested while loop the variable doesn't need to be initialized. + * @param isXml Whether the reader while loop is for XML reading. + * @param whileBlock The consumer that adds deserialization logic into the while loop. + */ + private void addReaderWhileLoop(String readerVariableName, JavaBlock methodBlock, + boolean initializeFieldNameVariable, boolean isXml, Consumer whileBlock) { + String whileCheck = isXml + ? readerVariableName + ".nextElement() != XmlToken.END_ELEMENT" + : readerVariableName + ".nextToken() != JsonToken.END_OBJECT"; + String fieldNameVariableName = isXml + ? propertiesManager.getXmlReaderNameVariableName() + : propertiesManager.getJsonReaderFieldNameVariableName(); + + methodBlock.block("while (" + whileCheck + ")", whileAction -> { + String fieldNameInitialization = ""; + if (initializeFieldNameVariable) { + fieldNameInitialization = isXml ? "QName" : "String"; + } - if (!isXml) { - methodBlock.line(readerVariableName + ".nextToken();"); - } - methodBlock.line(""); + methodBlock.line("%s %s = %s.get%sName();", fieldNameInitialization, fieldNameVariableName, + readerVariableName, isXml ? "Element" : "Field"); - whileBlock.accept(methodBlock); - }); - } + if (!isXml) { + methodBlock.line(readerVariableName + ".nextToken();"); + } + methodBlock.line(""); - private static void handleJsonPropertyDeserialization(ClientModel model, ClientModelProperty property, - String modelVariableName, JavaBlock methodBlock, AtomicReference ifBlockReference, - String fieldNameVariableName, boolean fromSuper, boolean hasConstructorArguments, JavaSettings settings, - boolean polymorphicJsonMergePatchScenario, boolean isFromJsonShared) { - // Property will be handled later by flattened deserialization. - if (property.getNeedsFlatten()) { - return; + whileBlock.accept(methodBlock); + }); } - JavaIfBlock ifBlock = ifBlockReference.get(); - ifBlock = handleJsonPropertyDeserialization(model, property, modelVariableName, methodBlock, ifBlock, - fieldNameVariableName, fromSuper, hasConstructorArguments, settings, polymorphicJsonMergePatchScenario, - isFromJsonShared); - - ifBlockReference.set(ifBlock); - } + private void handleJsonPropertyDeserialization(ClientModelProperty property, JavaBlock methodBlock, + AtomicReference ifBlockReference, boolean fromSuper, boolean isFromJsonShared) { + // Property will be handled later by flattened deserialization. + if (property.getNeedsFlatten()) { + return; + } - private static JavaIfBlock handleJsonPropertyDeserialization(ClientModel model, ClientModelProperty property, - String modelVariableName, JavaBlock methodBlock, JavaIfBlock ifBlock, String fieldNameVariableName, - boolean fromSuper, boolean hasConstructorArguments, JavaSettings settings, - boolean polymorphicJsonMergePatchScenario, boolean isFromJsonShared) { - String jsonPropertyName = property.getSerializedName(); - if (CoreUtils.isNullOrEmpty(jsonPropertyName)) { - return ifBlock; - } - - return ifOrElseIf(methodBlock, ifBlock, "\"" + jsonPropertyName + "\".equals(" + fieldNameVariableName + ")", - deserializationBlock -> { - generateJsonDeserializationLogic(deserializationBlock, modelVariableName, model, property, fromSuper, - hasConstructorArguments, settings, polymorphicJsonMergePatchScenario); - if (isFromJsonShared) { - deserializationBlock.methodReturn("true"); - } - }); - } + JavaIfBlock ifBlock = ifBlockReference.get(); + ifBlock = handleJsonPropertyDeserialization(property, methodBlock, ifBlock, fromSuper, isFromJsonShared); - private static void handleFlattenedPropertiesDeserialization( - JsonFlattenedPropertiesTree flattenedProperties, JavaBlock methodBlock, JavaIfBlock ifBlock, - ClientModelProperty additionalProperties, String fieldNameVariableName, boolean hasConstructorArguments, - JavaSettings settings, boolean polymorphicJsonMergePatchScenario, boolean isFromJsonShared) { - // The initial call to handle flattened properties is using the base node which is just a holder. - for (JsonFlattenedPropertiesTree structure : flattenedProperties.getChildrenNodes().values()) { - handleFlattenedPropertiesDeserializationHelper(structure, methodBlock, ifBlock, additionalProperties, - fieldNameVariableName, hasConstructorArguments, settings, polymorphicJsonMergePatchScenario, - isFromJsonShared, 0); + ifBlockReference.set(ifBlock); } - } - private static JavaIfBlock handleFlattenedPropertiesDeserializationHelper( - JsonFlattenedPropertiesTree flattenedProperties, JavaBlock methodBlock, JavaIfBlock ifBlock, - ClientModelProperty additionalProperties, String fieldNameVariableName, boolean hasConstructorArguments, - JavaSettings settings, boolean polymorphicJsonMergePatchScenario, boolean isFromJsonShared, int depth) { - ClientModelPropertyWithMetadata propertyWithMetadata = flattenedProperties.getProperty(); - if (propertyWithMetadata != null) { - String modelVariableName = "deserialized" + propertyWithMetadata.getModel().getName(); + private JavaIfBlock handleJsonPropertyDeserialization(ClientModelProperty property, JavaBlock methodBlock, + JavaIfBlock ifBlock, boolean fromSuper, boolean isFromJsonShared) { + String jsonPropertyName = property.getSerializedName(); + if (CoreUtils.isNullOrEmpty(jsonPropertyName)) { + return ifBlock; + } - // This is a terminal location, so only need to handle checking for the property name. return ifOrElseIf(methodBlock, ifBlock, - "\"" + flattenedProperties.getNodeName() + "\".equals(" + fieldNameVariableName + ")", - deserializationBlock -> generateJsonDeserializationLogic(deserializationBlock, modelVariableName, - propertyWithMetadata.getModel(), propertyWithMetadata.getProperty(), - propertyWithMetadata.isFromSuperClass(), hasConstructorArguments, settings, - polymorphicJsonMergePatchScenario)); - } else { - // Otherwise this is an intermediate location and a while loop reader needs to be added. - return ifOrElseIf(methodBlock, ifBlock, - "\"" + flattenedProperties.getNodeName() + "\".equals(" + fieldNameVariableName + ") && reader.currentToken() == JsonToken.START_OBJECT", - ifAction -> { - addReaderWhileLoop(ifAction, false, fieldNameVariableName, false, whileBlock -> { - JavaIfBlock innerIfBlock = null; - for (JsonFlattenedPropertiesTree structure : flattenedProperties.getChildrenNodes().values()) { - innerIfBlock = handleFlattenedPropertiesDeserializationHelper(structure, methodBlock, - innerIfBlock, additionalProperties, fieldNameVariableName, hasConstructorArguments, - settings, polymorphicJsonMergePatchScenario, isFromJsonShared, depth + 1); - } - - handleUnknownJsonFieldDeserialization(whileBlock, innerIfBlock, additionalProperties, - fieldNameVariableName); - }); - - if (isFromJsonShared && depth == 0) { - // Flattening will handle skipping and additional properties itself. - ifAction.methodReturn("true"); + "\"" + jsonPropertyName + "\".equals(" + propertiesManager.getJsonReaderFieldNameVariableName() + ")", + deserializationBlock -> { + generateJsonDeserializationLogic(deserializationBlock, property, fromSuper); + if (isFromJsonShared) { + deserializationBlock.methodReturn("true"); } }); } - } - - private static void generateJsonDeserializationLogic(JavaBlock deserializationBlock, String modelVariableName, - ClientModel model, ClientModelProperty property, boolean fromSuper, boolean hasConstructorArguments, JavaSettings settings, - boolean polymorphicJsonMergePatchScenario) { - IType wireType = property.getWireType(); - IType clientType = property.getClientType(); - - // Attempt to determine whether the wire type is simple deserialization. - // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. - String simpleDeserialization = getSimpleJsonDeserialization(wireType, "reader"); - if (simpleDeserialization != null) { - // Need to convert the wire type to the client type for constructors. - // Need to convert the wire type to the client type for public setters. - boolean convertToClientType = (clientType != wireType) - && (includePropertyInConstructor(property, settings) || (fromSuper && !ClientModelUtil.readOnlyNotInCtor(model, property, settings))); - BiConsumer simpleDeserializationConsumer = (logic, block) -> { - if (!hasConstructorArguments) { - handleSettingDeserializedValue(block, modelVariableName, model, property, logic, fromSuper, - polymorphicJsonMergePatchScenario); - } else { - block.line(property.getName() + " = " + logic + ";"); - } - }; - - if (convertToClientType) { - // If the wire type is nullable don't attempt to call the convert to client type until it's known that - // a value was deserialized. This protects against cases such as UnixTimeLong where the wire type is - // Long and the client type of OffsetDateTime. This is converted using Instant.ofEpochMilli(long) which - // would result in a null if the Long is null, which is already guarded using - // reader.readNullable(nonNullReader -> Instant.ofEpochMillis(nonNullReader.readLong())) but this itself - // returns null which would have been passed to OffsetDateTime.ofInstant(Instant, ZoneId) which would - // have thrown a NullPointerException. - if (wireType.isNullable()) { - // Check if the property is required, if so use a holder name as there will be an existing holder - // variable for the value that will be used in the constructor. - String holderName = property.getName() + "Holder"; - deserializationBlock.line(wireType + " " + holderName + " = " + simpleDeserialization + ";"); - deserializationBlock.ifBlock(holderName + " != null", ifBlock -> - simpleDeserializationConsumer.accept(wireType.convertToClientType(holderName), ifBlock)); - } else { - simpleDeserializationConsumer.accept(wireType.convertToClientType(simpleDeserialization), - deserializationBlock); - } - } else { - simpleDeserializationConsumer.accept(simpleDeserialization, deserializationBlock); - } - } else if (wireType == ClassType.OBJECT) { - if (!hasConstructorArguments) { - handleSettingDeserializedValue(deserializationBlock, modelVariableName, model, property, - "reader.readUntyped()", fromSuper, polymorphicJsonMergePatchScenario); - } else { - deserializationBlock.line(property.getName() + " = reader.readUntyped();"); - } - } else if (wireType instanceof IterableType) { - if (!hasConstructorArguments) { - deserializationBlock.text(property.getClientType() + " "); - } - - deserializationBlock.text(property.getName() + " = "); - deserializeJsonContainerProperty(deserializationBlock, "readArray", wireType, - ((IterableType) wireType).getElementType(), ((IterableType) clientType).getElementType(), 0); - if (!hasConstructorArguments) { - handleSettingDeserializedValue(deserializationBlock, modelVariableName, model, property, - property.getName(), fromSuper, polymorphicJsonMergePatchScenario); + private void handleFlattenedPropertiesDeserialization(JavaBlock methodBlock, JavaIfBlock ifBlock, boolean isFromJsonShared) { + // The initial call to handle flattened properties is using the base node which is just a holder. + for (JsonFlattenedPropertiesTree structure : propertiesManager.getJsonFlattenedPropertiesTree().getChildrenNodes().values()) { + handleFlattenedPropertiesDeserializationHelper(structure, methodBlock, ifBlock, isFromJsonShared, 0); } - } else if (wireType instanceof MapType) { - if (!hasConstructorArguments) { - deserializationBlock.text(property.getClientType() + " "); - } - - // Assumption is that the key type for the Map is a String. This may not always hold true and when that - // becomes reality this will need to be reworked to handle that case. - deserializationBlock.text(property.getName() + " = "); - deserializeJsonContainerProperty(deserializationBlock, "readMap", wireType, - ((MapType) wireType).getValueType(), ((MapType) clientType).getValueType(), 0); + } - if (!hasConstructorArguments) { - handleSettingDeserializedValue(deserializationBlock, modelVariableName, model, property, - property.getName(), fromSuper, polymorphicJsonMergePatchScenario); + private JavaIfBlock handleFlattenedPropertiesDeserializationHelper( + JsonFlattenedPropertiesTree flattenedProperties, JavaBlock methodBlock, JavaIfBlock ifBlock, + boolean isFromJsonShared, int depth) { + String fieldNameVariableName = propertiesManager.getJsonReaderFieldNameVariableName(); + ClientModelPropertyWithMetadata propertyWithMetadata = flattenedProperties.getProperty(); + if (propertyWithMetadata != null) { + // This is a terminal location, so only need to handle checking for the property name. + return ifOrElseIf(methodBlock, ifBlock, + "\"" + flattenedProperties.getNodeName() + "\".equals(" + fieldNameVariableName + ")", + deserializationBlock -> generateJsonDeserializationLogic(deserializationBlock, + propertyWithMetadata.getProperty(), propertyWithMetadata.isFromSuperClass())); + } else { + // Otherwise this is an intermediate location and a while loop reader needs to be added. + return ifOrElseIf(methodBlock, ifBlock, + "\"" + flattenedProperties.getNodeName() + "\".equals(" + fieldNameVariableName + + ") && reader.currentToken() == JsonToken.START_OBJECT", ifAction -> { + addReaderWhileLoop(ifAction, false, false, whileBlock -> { + JavaIfBlock innerIfBlock = null; + for (JsonFlattenedPropertiesTree structure : flattenedProperties.getChildrenNodes().values()) { + innerIfBlock = handleFlattenedPropertiesDeserializationHelper(structure, methodBlock, + innerIfBlock, isFromJsonShared, depth + 1); + } + + handleUnknownJsonFieldDeserialization(whileBlock, innerIfBlock, + propertiesManager.getAdditionalProperties()); + }); + + if (isFromJsonShared && depth == 0) { + // Flattening will handle skipping and additional properties itself. + ifAction.methodReturn("true"); + } + }); } - } else { - // TODO (alzimmer): Resolve this as deserialization logic generation needs to handle all cases. - throw new RuntimeException("Unknown wire type " + wireType + ". Need to add support for it."); } - // If the property was required, mark it as found. - if (includePropertyInConstructor(property, settings) && !settings.isDisableRequiredJsonAnnotation()) { - deserializationBlock.line(property.getName() + "Found = true;"); - } - } + private void generateJsonDeserializationLogic(JavaBlock deserializationBlock, ClientModelProperty property, + boolean fromSuper) { + IType wireType = property.getWireType(); + IType clientType = property.getClientType(); + + // Attempt to determine whether the wire type is simple deserialization. + // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. + String simpleDeserialization = getSimpleJsonDeserialization(wireType, "reader"); + if (simpleDeserialization != null) { + // Need to convert the wire type to the client type for constructors. + // Need to convert the wire type to the client type for public setters. + boolean convertToClientType = (clientType != wireType) && ( + includePropertyInConstructor(property, settings) || (fromSuper + && !ClientModelUtil.readOnlyNotInCtor(model, property, settings))); + BiConsumer simpleDeserializationConsumer = (logic, block) -> { + if (!propertiesManager.hasConstructorArguments()) { + handleSettingDeserializedValue(block, property, logic, fromSuper); + } else { + block.line(property.getName() + " = " + logic + ";"); + } + }; - /** - * Helper method to deserialize a container property (such as {@link List} and {@link Map}). - * - * @param methodBlock The method handling deserialization. - * @param utilityMethod The method aiding in the deserialization of the container. - * @param containerType The container type. - * @param elementWireType The element type for the container, for a {@link List} this is the element type and for a - * {@link Map} this is the value type. - * @param depth Depth of recursion for container types, such as {@code Map>} would be 0 when - * {@code Map} is being handled and then 1 when {@code List} is being handled. - */ - private static void deserializeJsonContainerProperty(JavaBlock methodBlock, String utilityMethod, IType containerType, - IType elementWireType, IType elementClientType, int depth) { - String callingReaderName = depth == 0 ? "reader" : "reader" + depth; - String lambdaReaderName = "reader" + (depth + 1); - String valueDeserializationMethod = getSimpleJsonDeserialization(elementWireType, lambdaReaderName); - boolean convertToClientType = (elementClientType != elementWireType); - boolean useCodeBlockLambda = valueDeserializationMethod != null && elementWireType.isNullable() - && convertToClientType; - - if (useCodeBlockLambda) { - methodBlock.line(callingReaderName + "." + utilityMethod + "(" + lambdaReaderName + " -> {"); - } else { - methodBlock.line(callingReaderName + "." + utilityMethod + "(" + lambdaReaderName + " ->"); - } - methodBlock.indent(() -> { - if (valueDeserializationMethod != null) { if (convertToClientType) { // If the wire type is nullable don't attempt to call the convert to client type until it's known that // a value was deserialized. This protects against cases such as UnixTimeLong where the wire type is @@ -1612,926 +1490,964 @@ private static void deserializeJsonContainerProperty(JavaBlock methodBlock, Stri // reader.readNullable(nonNullReader -> Instant.ofEpochMillis(nonNullReader.readLong())) but this itself // returns null which would have been passed to OffsetDateTime.ofInstant(Instant, ZoneId) which would // have thrown a NullPointerException. - if (elementWireType.isNullable()) { + if (wireType.isNullable()) { // Check if the property is required, if so use a holder name as there will be an existing holder // variable for the value that will be used in the constructor. - String holderName = lambdaReaderName + "ValueHolder"; - methodBlock.line(elementWireType + " " + holderName + " = " + valueDeserializationMethod + ";"); - methodBlock.ifBlock(holderName + " != null", - ifBlock -> ifBlock.methodReturn(elementWireType.convertToClientType(holderName))) - .elseBlock(elseBlock -> elseBlock.methodReturn("null")); + String holderName = property.getName() + "Holder"; + deserializationBlock.line(wireType + " " + holderName + " = " + simpleDeserialization + ";"); + deserializationBlock.ifBlock(holderName + " != null", + ifBlock -> simpleDeserializationConsumer.accept(wireType.convertToClientType(holderName), + ifBlock)); } else { - methodBlock.line(elementWireType.convertToClientType(valueDeserializationMethod)); + simpleDeserializationConsumer.accept(wireType.convertToClientType(simpleDeserialization), + deserializationBlock); } } else { - methodBlock.line(valueDeserializationMethod); + simpleDeserializationConsumer.accept(simpleDeserialization, deserializationBlock); + } + } else if (wireType == ClassType.OBJECT) { + if (!propertiesManager.hasConstructorArguments()) { + handleSettingDeserializedValue(deserializationBlock, property, "reader.readUntyped()", fromSuper); + } else { + deserializationBlock.line(property.getName() + " = reader.readUntyped();"); + } + } else if (wireType instanceof IterableType) { + if (!propertiesManager.hasConstructorArguments()) { + deserializationBlock.text(property.getClientType() + " "); + } + + deserializationBlock.text(property.getName() + " = "); + deserializeJsonContainerProperty(deserializationBlock, "readArray", wireType, + ((IterableType) wireType).getElementType(), ((IterableType) clientType).getElementType(), 0); + + if (!propertiesManager.hasConstructorArguments()) { + handleSettingDeserializedValue(deserializationBlock, property, property.getName(), fromSuper); + } + } else if (wireType instanceof MapType) { + if (!propertiesManager.hasConstructorArguments()) { + deserializationBlock.text(property.getClientType() + " "); } - } else if (elementWireType == ClassType.OBJECT) { - methodBlock.line(lambdaReaderName + ".readUntyped()"); - } else if (elementWireType instanceof IterableType) { - deserializeJsonContainerProperty(methodBlock, "readArray", elementWireType, - ((IterableType) elementWireType).getElementType(), - ((IterableType) elementClientType).getElementType(), depth + 1); - } else if (elementWireType instanceof MapType) { + // Assumption is that the key type for the Map is a String. This may not always hold true and when that // becomes reality this will need to be reworked to handle that case. - deserializeJsonContainerProperty(methodBlock, "readMap", elementWireType, - ((MapType) elementWireType).getValueType(), ((MapType) elementClientType).getValueType(), - depth + 1); - } else if (elementWireType == ClassType.BINARY_DATA) { - methodBlock.line(lambdaReaderName + ".readUntyped()"); - } else { - throw new RuntimeException("Unknown value type " + elementWireType + " in " + containerType - + " serialization. Need to add support for it."); - } - }); + deserializationBlock.text(property.getName() + " = "); + deserializeJsonContainerProperty(deserializationBlock, "readMap", wireType, + ((MapType) wireType).getValueType(), ((MapType) clientType).getValueType(), 0); - if (useCodeBlockLambda) { - if (depth > 0) { - methodBlock.line("})"); + if (!propertiesManager.hasConstructorArguments()) { + handleSettingDeserializedValue(deserializationBlock, property, property.getName(), fromSuper); + } } else { - methodBlock.line("});"); + // TODO (alzimmer): Resolve this as deserialization logic generation needs to handle all cases. + throw new RuntimeException("Unknown wire type " + wireType + ". Need to add support for it."); } - } else { - if (depth > 0) { - methodBlock.line(")"); - } else { - methodBlock.line(");"); + + // If the property was required, mark it as found. + if (includePropertyInConstructor(property, settings) && !settings.isDisableRequiredJsonAnnotation()) { + deserializationBlock.line(property.getName() + "Found = true;"); } } - } - private static String getSimpleJsonDeserialization(IType wireType, String readerName) { - return (wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) - ? wireType + ".fromJson(" + readerName + ")" - : wireType.jsonDeserializationMethod(readerName); - } + /** + * Helper method to deserialize a container property (such as {@link List} and {@link Map}). + * + * @param methodBlock The method handling deserialization. + * @param utilityMethod The method aiding in the deserialization of the container. + * @param containerType The container type. + * @param elementWireType The element type for the container, for a {@link List} this is the element type and + * for a {@link Map} this is the value type. + * @param depth Depth of recursion for container types, such as {@code Map>} would be 0 + * when {@code Map} is being handled and then 1 when {@code List} is being handled. + */ + private static void deserializeJsonContainerProperty(JavaBlock methodBlock, String utilityMethod, + IType containerType, IType elementWireType, IType elementClientType, int depth) { + String callingReaderName = depth == 0 ? "reader" : "reader" + depth; + String lambdaReaderName = "reader" + (depth + 1); + String valueDeserializationMethod = getSimpleJsonDeserialization(elementWireType, lambdaReaderName); + boolean convertToClientType = (elementClientType != elementWireType); + boolean useCodeBlockLambda = valueDeserializationMethod != null && elementWireType.isNullable() + && convertToClientType; + + if (useCodeBlockLambda) { + methodBlock.line(callingReaderName + "." + utilityMethod + "(" + lambdaReaderName + " -> {"); + } else { + methodBlock.line(callingReaderName + "." + utilityMethod + "(" + lambdaReaderName + " ->"); + } + methodBlock.indent(() -> { + if (valueDeserializationMethod != null) { + if (convertToClientType) { + // If the wire type is nullable don't attempt to call the convert to client type until it's known that + // a value was deserialized. This protects against cases such as UnixTimeLong where the wire type is + // Long and the client type of OffsetDateTime. This is converted using Instant.ofEpochMilli(long) which + // would result in a null if the Long is null, which is already guarded using + // reader.readNullable(nonNullReader -> Instant.ofEpochMillis(nonNullReader.readLong())) but this itself + // returns null which would have been passed to OffsetDateTime.ofInstant(Instant, ZoneId) which would + // have thrown a NullPointerException. + if (elementWireType.isNullable()) { + // Check if the property is required, if so use a holder name as there will be an existing holder + // variable for the value that will be used in the constructor. + String holderName = lambdaReaderName + "ValueHolder"; + methodBlock.line( + elementWireType + " " + holderName + " = " + valueDeserializationMethod + ";"); + methodBlock.ifBlock(holderName + " != null", + ifBlock -> ifBlock.methodReturn(elementWireType.convertToClientType(holderName))) + .elseBlock(elseBlock -> elseBlock.methodReturn("null")); + } else { + methodBlock.line(elementWireType.convertToClientType(valueDeserializationMethod)); + } + } else { + methodBlock.line(valueDeserializationMethod); + } + } else if (elementWireType == ClassType.OBJECT) { + methodBlock.line(lambdaReaderName + ".readUntyped()"); + } else if (elementWireType instanceof IterableType) { + deserializeJsonContainerProperty(methodBlock, "readArray", elementWireType, + ((IterableType) elementWireType).getElementType(), + ((IterableType) elementClientType).getElementType(), depth + 1); + } else if (elementWireType instanceof MapType) { + // Assumption is that the key type for the Map is a String. This may not always hold true and when that + // becomes reality this will need to be reworked to handle that case. + deserializeJsonContainerProperty(methodBlock, "readMap", elementWireType, + ((MapType) elementWireType).getValueType(), ((MapType) elementClientType).getValueType(), + depth + 1); + } else if (elementWireType == ClassType.BINARY_DATA) { + methodBlock.line(lambdaReaderName + ".readUntyped()"); + } else { + throw new RuntimeException("Unknown value type " + elementWireType + " in " + containerType + + " serialization. Need to add support for it."); + } + }); - private static void handleUnknownJsonFieldDeserialization(JavaBlock methodBlock, JavaIfBlock ifBlock, - ClientModelProperty additionalProperties, String fieldNameVariableName) { - Consumer unknownFieldConsumer = javaBlock -> { - if (additionalProperties != null) { - javaBlock.ifBlock(additionalProperties.getName() + " == null", - ifAction -> ifAction.line(additionalProperties.getName() + " = new LinkedHashMap<>();")); - javaBlock.line(); - - // Assumption, additional properties is a Map of String-Object - IType valueType = ((MapType) additionalProperties.getWireType()).getValueType(); - if (valueType == ClassType.OBJECT) { - // String fieldName should be a local variable accessible in this spot of code. - javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + ", reader.readUntyped());"); - } else if (valueType instanceof IterableType) { - // The case that element is a List - String varName = additionalProperties.getName() + "ArrayItem"; - javaBlock.text(valueType + " " + varName + " = "); - deserializeJsonContainerProperty(javaBlock, "readArray", valueType, - ((IterableType) valueType).getElementType(), ((IterableType) valueType).getElementType(), 0); - javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + ", " + varName + ");"); + if (useCodeBlockLambda) { + if (depth > 0) { + methodBlock.line("})"); } else { - // Another assumption, the additional properties value type is simple. - javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + ", " - + getSimpleJsonDeserialization(valueType, "reader") + ");"); + methodBlock.line("});"); } } else { - javaBlock.line("reader.skipChildren();"); + if (depth > 0) { + methodBlock.line(")"); + } else { + methodBlock.line(");"); + } } - }; - - if (ifBlock == null) { - unknownFieldConsumer.accept(methodBlock); - } else { - ifBlock.elseBlock(unknownFieldConsumer); } - } - /** - * Handles validating that all required properties have been found and creating the return type. - *

- * Properties are split into two concepts, required and optional properties, and those concepts are split into an - * additional two groups, properties declared by super types and by the model type. - * - * @param methodBlock The method handling deserialization. - * @param modelName The name of the model. - * @param propertiesManager The property manager for the model. - */ - private static void handleReadReturn(JavaBlock methodBlock, String modelName, - ClientModelPropertiesManager propertiesManager, JavaSettings settings) { - StringBuilder constructorArgs = new StringBuilder(); - - propertiesManager.forEachSuperConstructorProperty(arg -> addConstructorParameter(constructorArgs, arg.getName())); - propertiesManager.forEachConstructorProperty(arg -> addConstructorParameter(constructorArgs, arg.getName())); - - // If there are required properties of any type we must check that all required fields were found. - if (propertiesManager.hasRequiredProperties()) { - StringBuilder ifStatementBuilder = new StringBuilder(); - propertiesManager.forEachSuperRequiredProperty(property -> addRequiredCheck(ifStatementBuilder, property, settings)); - propertiesManager.forEachRequiredProperty(property -> addRequiredCheck(ifStatementBuilder, property, settings)); - - if (ifStatementBuilder.length() > 0) { - methodBlock.ifBlock(ifStatementBuilder.toString(), ifAction -> - createObjectAndReturn(methodBlock, modelName, constructorArgs.toString(), propertiesManager, settings)); - - if (propertiesManager.getRequiredPropertiesCount() == 1) { - StringBuilder stringBuilder = new StringBuilder(); - propertiesManager.forEachSuperRequiredProperty(property -> stringBuilder.append(property.getSerializedName())); - propertiesManager.forEachRequiredProperty(property -> stringBuilder.append(property.getSerializedName())); - methodBlock.line("throw new IllegalStateException(\"Missing required property: " + stringBuilder + "\");"); + private static String getSimpleJsonDeserialization(IType wireType, String readerName) { + return (wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) ? wireType + ".fromJson(" + + readerName + ")" : wireType.jsonDeserializationMethod(readerName); + } + + private void handleUnknownJsonFieldDeserialization(JavaBlock methodBlock, JavaIfBlock ifBlock, + ClientModelProperty additionalProperties) { + String fieldNameVariableName = propertiesManager.getJsonReaderFieldNameVariableName(); + Consumer unknownFieldConsumer = javaBlock -> { + if (additionalProperties != null) { + javaBlock.ifBlock(additionalProperties.getName() + " == null", + ifAction -> ifAction.line(additionalProperties.getName() + " = new LinkedHashMap<>();")); + javaBlock.line(); + + // Assumption, additional properties is a Map of String-Object + IType valueType = ((MapType) additionalProperties.getWireType()).getValueType(); + if (valueType == ClassType.OBJECT) { + // String fieldName should be a local variable accessible in this spot of code. + javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + + ", reader.readUntyped());"); + } else if (valueType instanceof IterableType) { + // The case that element is a List + String varName = additionalProperties.getName() + "ArrayItem"; + javaBlock.text(valueType + " " + varName + " = "); + deserializeJsonContainerProperty(javaBlock, "readArray", valueType, + ((IterableType) valueType).getElementType(), ((IterableType) valueType).getElementType(), + 0); + javaBlock.line( + additionalProperties.getName() + ".put(" + fieldNameVariableName + ", " + varName + ");"); + } else { + // Another assumption, the additional properties value type is simple. + javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + ", " + + getSimpleJsonDeserialization(valueType, "reader") + ");"); + } } else { - methodBlock.line("List missingProperties = new ArrayList<>();"); - propertiesManager.forEachSuperRequiredProperty(property -> addFoundValidationIfCheck(methodBlock, property, settings)); - propertiesManager.forEachRequiredProperty(property -> addFoundValidationIfCheck(methodBlock, property, settings)); - - methodBlock.line(); - methodBlock.line("throw new IllegalStateException(\"Missing required property/properties: \" + String.join(\", \", missingProperties));"); + javaBlock.line("reader.skipChildren();"); } - } else { - createObjectAndReturn(methodBlock, modelName, constructorArgs.toString(), propertiesManager, settings); - } - } else { - createObjectAndReturn(methodBlock, modelName, constructorArgs.toString(), propertiesManager, settings); - } - } + }; - private static void createObjectAndReturn(JavaBlock methodBlock, String modelName, String constructorArgs, - ClientModelPropertiesManager propertiesManager, JavaSettings settings) { - boolean polymorphicJsonMergePatchScenario = propertiesManager.getModel().isPolymorphic() - && ClientModelUtil.isJsonMergePatchModel(propertiesManager.getModel(), settings); - if (propertiesManager.hasConstructorArguments()) { - if (propertiesManager.getSetterPropertiesCount() == 0 - && propertiesManager.getReadOnlyPropertiesCount() == 0 - && propertiesManager.getAdditionalProperties() == null - && propertiesManager.getSuperAdditionalPropertiesProperty() == null) { - methodBlock.methodReturn("new " + modelName + "(" + constructorArgs + ")"); - return; + if (ifBlock == null) { + unknownFieldConsumer.accept(methodBlock); + } else { + ifBlock.elseBlock(unknownFieldConsumer); } - - methodBlock.line(modelName + " " + propertiesManager.getDeserializedModelName() + " = new " + modelName - + "(" + constructorArgs + ");"); - - BiConsumer handleSettingDeserializedValue = (property, fromSuper) -> - handleSettingDeserializedValue(methodBlock, propertiesManager.getDeserializedModelName(), - propertiesManager.getModel(), property, property.getName(), fromSuper, - polymorphicJsonMergePatchScenario); - - propertiesManager.forEachSuperReadOnlyProperty(property -> handleSettingDeserializedValue.accept(property, true)); - propertiesManager.forEachSuperSetterProperty(property -> handleSettingDeserializedValue.accept(property, true)); - propertiesManager.forEachReadOnlyProperty(property -> handleSettingDeserializedValue.accept(property, false)); - propertiesManager.forEachSetterProperty(property -> handleSettingDeserializedValue.accept(property, false)); - } - - if (propertiesManager.getAdditionalProperties() != null) { - handleSettingDeserializedValue(methodBlock, propertiesManager.getDeserializedModelName(), - propertiesManager.getModel(), propertiesManager.getAdditionalProperties(), - propertiesManager.getAdditionalProperties().getName(), false, polymorphicJsonMergePatchScenario); - } else if (propertiesManager.getSuperAdditionalPropertiesProperty() != null) { - handleSettingDeserializedValue(methodBlock, propertiesManager.getDeserializedModelName(), - propertiesManager.getModel(), propertiesManager.getSuperAdditionalPropertiesProperty(), - propertiesManager.getSuperAdditionalPropertiesProperty().getName(), true, - polymorphicJsonMergePatchScenario); - } - - methodBlock.line(); - methodBlock.methodReturn(propertiesManager.getDeserializedModelName()); - } - - private static void addConstructorParameter(StringBuilder constructor, String parameterName) { - if (constructor.length() > 0) { - constructor.append(", "); - } - - constructor.append(parameterName); - } - - private static void addRequiredCheck(StringBuilder ifCheck, ClientModelProperty property, JavaSettings settings) { - // XML attributes and text don't need checks. - if (property.isXmlAttribute() || property.isXmlText() || !includePropertyInConstructor(property, settings)) { - return; - } - - // Constants are ignored during deserialization. - if (property.isConstant()) { - return; - } - - // Required properties aren't being validated for being found. - if (settings.isDisableRequiredJsonAnnotation()) { - return; - } - - if (ifCheck.length() > 0) { - ifCheck.append(" && "); - } - - ifCheck.append(property.getName()).append("Found"); - } - - private static void addFoundValidationIfCheck(JavaBlock methodBlock, ClientModelProperty property, - JavaSettings settings) { - // XML attributes and text don't need checks. - if (property.isXmlAttribute() || property.isXmlText() || !includePropertyInConstructor(property, settings)) { - return; - } - - // Constants are ignored during deserialization. - if (property.isConstant()) { - return; - } - - // Required properties aren't being validated for being found. - if (settings.isDisableRequiredJsonAnnotation()) { - return; } - methodBlock.ifBlock("!" + property.getName() + "Found", - ifAction -> ifAction.line("missingProperties.add(\"" + property.getSerializedName() + "\");")); - } - - private static void handleSettingDeserializedValue(JavaBlock methodBlock, String modelVariableName, - ClientModel model, ClientModelProperty property, String value, boolean fromSuper, - boolean polymorphicJsonMergePatchScenario) { - // If the property is defined in a super class use the setter as this will be able to set the value in the - // super class. - if (fromSuper - // If the property is flattened or read-only from parent, it will be shadowed in child class. - && (!ClientModelUtil.readOnlyNotInCtor(model, property, JavaSettings.getInstance()) && !property.getClientFlatten())) { - if (polymorphicJsonMergePatchScenario) { - // Polymorphic JSON merge patch needs special handling as the setter methods are used to track whether - // the property is included in patch serialization. To prevent deserialization from requiring parent - // defined properties to always be included in serialization, access helpers are used to set the value - // without marking the property as included in the patch. - ClientModel definingModel = definingModel(model, property); - methodBlock.line("JsonMergePatchHelper.get" + definingModel.getName() + "Accessor()." - + property.getSetterName() + "(" + modelVariableName + ", " + value + ");"); + /** + * Handles validating that all required properties have been found and creating the return type. + *

+ * Properties are split into two concepts, required and optional properties, and those concepts are split into + * an additional two groups, properties declared by super types and by the model type. + * + * @param methodBlock The method handling deserialization. + */ + private void handleReadReturn(JavaBlock methodBlock) { + StringBuilder constructorArgs = new StringBuilder(); + + propertiesManager.forEachSuperConstructorProperty( + arg -> addConstructorParameter(constructorArgs, arg.getName())); + propertiesManager.forEachConstructorProperty( + arg -> addConstructorParameter(constructorArgs, arg.getName())); + + // If there are required properties of any type we must check that all required fields were found. + if (propertiesManager.hasRequiredProperties()) { + StringBuilder ifStatementBuilder = new StringBuilder(); + propertiesManager.forEachSuperRequiredProperty( + property -> addRequiredCheck(ifStatementBuilder, property)); + propertiesManager.forEachRequiredProperty(property -> addRequiredCheck(ifStatementBuilder, property)); + + if (ifStatementBuilder.length() > 0) { + methodBlock.ifBlock(ifStatementBuilder.toString(), + ifAction -> createObjectAndReturn(methodBlock, constructorArgs.toString())); + + if (propertiesManager.getRequiredPropertiesCount() == 1) { + StringBuilder stringBuilder = new StringBuilder(); + propertiesManager.forEachSuperRequiredProperty( + property -> stringBuilder.append(property.getSerializedName())); + propertiesManager.forEachRequiredProperty( + property -> stringBuilder.append(property.getSerializedName())); + methodBlock.line( + "throw new IllegalStateException(\"Missing required property: " + stringBuilder + "\");"); + } else { + methodBlock.line("List missingProperties = new ArrayList<>();"); + propertiesManager.forEachSuperRequiredProperty( + property -> addFoundValidationIfCheck(methodBlock, property)); + propertiesManager.forEachRequiredProperty( + property -> addFoundValidationIfCheck(methodBlock, property)); + + methodBlock.line(); + methodBlock.line( + "throw new IllegalStateException(\"Missing required property/properties: \" + String.join(\", \", missingProperties));"); + } + } else { + createObjectAndReturn(methodBlock, constructorArgs.toString()); + } } else { - methodBlock.line(modelVariableName + "." + property.getSetterName() + "(" + value + ");"); + createObjectAndReturn(methodBlock, constructorArgs.toString()); } - } else { - methodBlock.line(modelVariableName + "." + property.getName() + " = " + value + ";"); } - } - private static boolean isSuperTypeWithDiscriminator(ClientModel child) { - return !CoreUtils.isNullOrEmpty(child.getPolymorphicDiscriminatorName()) - && !CoreUtils.isNullOrEmpty(child.getDerivedModels()); - } + private void createObjectAndReturn(JavaBlock methodBlock, String constructorArgs) { + if (propertiesManager.hasConstructorArguments()) { + if (propertiesManager.getSetterPropertiesCount() == 0 + && propertiesManager.getReadOnlyPropertiesCount() == 0 + && propertiesManager.getAdditionalProperties() == null + && propertiesManager.getSuperAdditionalPropertiesProperty() == null) { + methodBlock.methodReturn("new " + model.getName() + "(" + constructorArgs + ")"); + return; + } + + methodBlock.line(model.getName() + " " + propertiesManager.getDeserializedModelName() + " = new " + + model.getName() + "(" + constructorArgs + ");"); + + BiConsumer handleSettingDeserializedValue + = (property, fromSuper) -> handleSettingDeserializedValue(methodBlock, property, property.getName(), + fromSuper); + + propertiesManager.forEachSuperReadOnlyProperty( + property -> handleSettingDeserializedValue.accept(property, true)); + propertiesManager.forEachSuperSetterProperty( + property -> handleSettingDeserializedValue.accept(property, true)); + propertiesManager.forEachReadOnlyProperty( + property -> handleSettingDeserializedValue.accept(property, false)); + propertiesManager.forEachSetterProperty( + property -> handleSettingDeserializedValue.accept(property, false)); + } - // TODO (alzimmer): This is a very inefficient design where we're using the ClientModelProperty to find which model - // in the polymorphic hierarchy defines it, but this is a simple bootstrapping method rather than a larger - // re-architecting. - private static ClientModel definingModel(ClientModel model, ClientModelProperty property) { - while (model != null) { - if (model.getProperties().stream().anyMatch(prop -> Objects.equals(prop.getName(), property.getName()))) { - return model; + if (propertiesManager.getAdditionalProperties() != null) { + handleSettingDeserializedValue(methodBlock, propertiesManager.getAdditionalProperties(), + propertiesManager.getAdditionalProperties().getName(), false); + } else if (propertiesManager.getSuperAdditionalPropertiesProperty() != null) { + handleSettingDeserializedValue(methodBlock, propertiesManager.getSuperAdditionalPropertiesProperty(), + propertiesManager.getSuperAdditionalPropertiesProperty().getName(), true); } - model = ClientModelUtil.getClientModel(model.getParentModelName()); + + methodBlock.line(); + methodBlock.methodReturn(propertiesManager.getDeserializedModelName()); } - throw new IllegalStateException("No ClientModel in the polymorphic hierarchy define this property, this should never happen."); - } + private static void addConstructorParameter(StringBuilder constructor, String parameterName) { + if (constructor.length() > 0) { + constructor.append(", "); + } - /** - * Helper method for adding a base if condition or an else if condition. - * - * @param baseBlock Base code block where an if condition would be added. - * @param ifBlock If block where an else if condition would be added. - * @param condition The conditional statement. - * @param action The conditional action. - * @return An if block for further chaining. - */ - private static JavaIfBlock ifOrElseIf(JavaBlock baseBlock, JavaIfBlock ifBlock, String condition, - Consumer action) { - return (ifBlock == null) ? baseBlock.ifBlock(condition, action) : ifBlock.elseIfBlock(condition, action); - } + constructor.append(parameterName); + } - private static void writeToXml(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - Consumer addGeneratedAnnotation) { - addGeneratedAnnotation.accept(classBlock); - classBlock.annotation("Override"); - classBlock.publicMethod("XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException", - methodBlock -> methodBlock.methodReturn("toXml(xmlWriter, null)")); + private void addRequiredCheck(StringBuilder ifCheck, ClientModelProperty property) { + // XML attributes and text don't need checks. + if (property.isXmlAttribute() || property.isXmlText() || !includePropertyInConstructor(property, + settings)) { + return; + } - addGeneratedAnnotation.accept(classBlock); - classBlock.annotation("Override"); - classBlock.publicMethod("XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException", methodBlock -> { - String modelXmlName = propertiesManager.getXmlRootElementName(); - methodBlock.line("rootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? \"" + modelXmlName + "\" : rootElementName;"); - methodBlock.line("xmlWriter.writeStartElement(rootElementName);"); - - String modelXmlNamespace = propertiesManager.getXmlRootElementNamespace(); - if (modelXmlNamespace != null) { - methodBlock.line("xmlWriter.writeNamespace(" + propertiesManager.getXmlNamespaceConstant(modelXmlNamespace) + ");"); + // Constants are ignored during deserialization. + if (property.isConstant()) { + return; } - propertiesManager.forEachXmlNamespaceWithPrefix((prefix, namespace) -> - methodBlock.line("xmlWriter.writeNamespace(\"" + prefix + "\", " + propertiesManager.getXmlNamespaceConstant(namespace) + ");")); + // Required properties aren't being validated for being found. + if (settings.isDisableRequiredJsonAnnotation()) { + return; + } - // Assumption for XML is polymorphic discriminators are attributes. - if (propertiesManager.getDiscriminatorProperty() != null) { - serializeXml(methodBlock, propertiesManager.getDiscriminatorProperty().getProperty(), false, - propertiesManager); + if (ifCheck.length() > 0) { + ifCheck.append(" && "); } - propertiesManager.forEachSuperXmlAttribute(property -> serializeXml(methodBlock, property, true, propertiesManager)); - propertiesManager.forEachXmlAttribute(property -> serializeXml(methodBlock, property, false, propertiesManager)); + ifCheck.append(property.getName()).append("Found"); + } - // Valid XML should only either have elements or text. - if (propertiesManager.hasXmlElements()) { - propertiesManager.forEachSuperXmlElement(property -> serializeXml(methodBlock, property, true, propertiesManager)); - propertiesManager.forEachXmlElement(property -> serializeXml(methodBlock, property, false, propertiesManager)); - } else { - propertiesManager.forEachSuperXmlText(property -> serializeXml(methodBlock, property, true, propertiesManager)); - propertiesManager.forEachXmlText(property -> serializeXml(methodBlock, property, false, propertiesManager)); + private void addFoundValidationIfCheck(JavaBlock methodBlock, ClientModelProperty property) { + // XML attributes and text don't need checks. + if (property.isXmlAttribute() || property.isXmlText() || !includePropertyInConstructor(property, + settings)) { + return; } - methodBlock.methodReturn("xmlWriter.writeEndElement()"); - }); - } + // Constants are ignored during deserialization. + if (property.isConstant()) { + return; + } - /** - * Serializes an XML element. - * - * @param methodBlock The method handling serialization. - * @param element The XML element being serialized. - * @param fromSuperType Whether the property is defined in the super type. - */ - private static void serializeXml(JavaBlock methodBlock, ClientModelProperty element, boolean fromSuperType, - ClientModelPropertiesManager propertiesManager) { - IType clientType = element.getClientType(); - IType wireType = element.getWireType(); - String propertyValueGetter; - if (fromSuperType) { - propertyValueGetter = (clientType != wireType) - ? wireType.convertFromClientType(element.getGetterName() + "()") - : element.getGetterName() + "()"; - } else { - propertyValueGetter = "this." + element.getName(); - } - - // Attempt to determine whether the wire type is simple serialization. - // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. - String xmlSerializationMethodCall = wireType.xmlSerializationMethodCall("xmlWriter", element.getXmlName(), - propertiesManager.getXmlNamespaceConstant(element.getXmlNamespace()), propertyValueGetter, - element.isXmlAttribute(), false, true); - if (xmlSerializationMethodCall != null) { - Consumer serializationLogic = javaBlock -> { - // XML text has special handling. - if (element.isXmlText()) { - javaBlock.line("xmlWriter.writeString(" + propertyValueGetter + ");"); + // Required properties aren't being validated for being found. + if (settings.isDisableRequiredJsonAnnotation()) { + return; + } + + methodBlock.ifBlock("!" + property.getName() + "Found", + ifAction -> ifAction.line("missingProperties.add(\"" + property.getSerializedName() + "\");")); + } + + private void handleSettingDeserializedValue(JavaBlock methodBlock, ClientModelProperty property, String value, + boolean fromSuper) { + String modelVariableName = propertiesManager.getDeserializedModelName(); + // If the property is defined in a super class use the setter as this will be able to set the value in the + // super class. + if (fromSuper + // If the property is flattened or read-only from parent, it will be shadowed in child class. + && (!ClientModelUtil.readOnlyNotInCtor(model, property, settings) && !property.getClientFlatten())) { + if (model.isPolymorphic() && isJsonMergePatchModel) { + // Polymorphic JSON merge patch needs special handling as the setter methods are used to track whether + // the property is included in patch serialization. To prevent deserialization from requiring parent + // defined properties to always be included in serialization, access helpers are used to set the value + // without marking the property as included in the patch. + ClientModel definingModel = ClientModelUtil.getDefiningModel(model, property); + methodBlock.line( + "JsonMergePatchHelper.get" + definingModel.getName() + "Accessor()." + property.getSetterName() + + "(" + modelVariableName + ", " + value + ");"); } else { - javaBlock.line(xmlSerializationMethodCall + ";"); + methodBlock.line(modelVariableName + "." + property.getSetterName() + "(" + value + ");"); } - }; - - // If the property is from a super type and the client type is different from the wire type then a null - // check is required to prevent a NullPointerException when converting the value. - if (fromSuperType && clientType != wireType && clientType.isNullable()) { - methodBlock.ifBlock(propertyValueGetter + " != null", serializationLogic); } else { - serializationLogic.accept(methodBlock); + methodBlock.line(modelVariableName + "." + property.getName() + " = " + value + ";"); } - } else if (wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { - methodBlock.line("xmlWriter.writeXml(" + propertyValueGetter + ");"); - } else if (wireType instanceof IterableType) { - IType elementType = ((IterableType) wireType).getElementType(); - - methodBlock.ifBlock(propertyValueGetter + " != null", ifAction -> { - if (element.isXmlWrapper()) { - String writeStartElement = element.getXmlNamespace() == null - ? "xmlWriter.writeStartElement(\"" + element.getXmlName() + "\");" - : "xmlWriter.writeStartElement(" + propertiesManager.getXmlNamespaceConstant(element.getXmlNamespace()) + ", \"" + element.getXmlName() + "\");"; - ifAction.line(writeStartElement); - } + } - String xmlWrite = elementType.xmlSerializationMethodCall("xmlWriter", element.getXmlListElementName(), - propertiesManager.getXmlNamespaceConstant(element.getXmlListElementNamespace()), "element", false, - false, true); - ifAction.line("for (%s element : %s) {", elementType, propertyValueGetter); - ifAction.indent(() -> ifAction.line(xmlWrite + ";")); - ifAction.line("}"); + private static boolean isSuperTypeWithDiscriminator(ClientModel child) { + return !CoreUtils.isNullOrEmpty(child.getPolymorphicDiscriminatorName()) && !CoreUtils.isNullOrEmpty( + child.getDerivedModels()); + } - if (element.isXmlWrapper()) { - ifAction.line("xmlWriter.writeEndElement();"); - } - }); - } else if (wireType instanceof MapType) { - // Assumption is that the key type for the Map is a String. This may not always hold true and when that - // becomes reality this will need to be reworked to handle that case. - IType valueType = ((MapType) wireType).getValueType(); + /** + * Helper method for adding a base if condition or an else if condition. + * + * @param baseBlock Base code block where an if condition would be added. + * @param ifBlock If block where an else if condition would be added. + * @param condition The conditional statement. + * @param action The conditional action. + * @return An if block for further chaining. + */ + private static JavaIfBlock ifOrElseIf(JavaBlock baseBlock, JavaIfBlock ifBlock, String condition, + Consumer action) { + return (ifBlock == null) ? baseBlock.ifBlock(condition, action) : ifBlock.elseIfBlock(condition, action); + } - methodBlock.ifBlock(propertyValueGetter + " != null", ifAction -> { - ifAction.line("xmlWriter.writeStartElement(\"" + element.getXmlName() + "\");"); + private void writeToXml(JavaClass classBlock) { + addGeneratedAnnotation.accept(classBlock); + classBlock.annotation("Override"); + classBlock.publicMethod("XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException", + methodBlock -> methodBlock.methodReturn("toXml(xmlWriter, null)")); - if (valueType instanceof ClassType && ((ClassType) valueType).isSwaggerType()) { - String writeStartElement = (element.getXmlNamespace() != null) - ? "xmlWriter.writeStartElement(" + propertiesManager.getXmlNamespaceConstant(element.getXmlNamespace()) + ", key);" - : "xmlWriter.writeStartElement(key);"; + addGeneratedAnnotation.accept(classBlock); + classBlock.annotation("Override"); + classBlock.publicMethod( + "XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException", + methodBlock -> { + String modelXmlName = propertiesManager.getXmlRootElementName(); + methodBlock.line("rootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? \"" + modelXmlName + + "\" : rootElementName;"); + methodBlock.line("xmlWriter.writeStartElement(rootElementName);"); + + String modelXmlNamespace = propertiesManager.getXmlRootElementNamespace(); + if (modelXmlNamespace != null) { + methodBlock.line( + "xmlWriter.writeNamespace(" + propertiesManager.getXmlNamespaceConstant(modelXmlNamespace) + + ");"); + } - ifAction.line("for (Map.Entry entry : %s.entrySet()) {", valueType, propertyValueGetter); - ifAction.indent(() -> { - ifAction.line(writeStartElement); - ifAction.line("xmlWriter.writeXml(entry.getValue());"); - ifAction.line("xmlWriter.writeEndElement();"); - }); - ifAction.line("}"); - } else { - String xmlWrite = valueType.xmlSerializationMethodCall("xmlWriter", "entry.getKey()", - propertiesManager.getXmlNamespaceConstant(element.getXmlNamespace()), "entry.getValue()", false, - true, true); + propertiesManager.forEachXmlNamespaceWithPrefix((prefix, namespace) -> methodBlock.line( + "xmlWriter.writeNamespace(\"" + prefix + "\", " + propertiesManager.getXmlNamespaceConstant( + namespace) + ");")); - ifAction.line("for (Map.Entry entry : %s.entrySet()) {", valueType, propertyValueGetter); - ifAction.indent(() -> ifAction.line(xmlWrite + ";")); - ifAction.line("}"); - } + // Assumption for XML is polymorphic discriminators are attributes. + if (propertiesManager.getDiscriminatorProperty() != null) { + serializeXml(methodBlock, propertiesManager.getDiscriminatorProperty().getProperty(), false); + } - ifAction.line("xmlWriter.writeEndElement();"); - }); - } else { - // TODO (alzimmer): Resolve this as serialization logic generation needs to handle all cases. - throw new RuntimeException("Unknown wire type " + wireType + " in XML element serialization. " - + "Need to add support for it."); - } - } + propertiesManager.forEachSuperXmlAttribute(property -> serializeXml(methodBlock, property, true)); + propertiesManager.forEachXmlAttribute(property -> serializeXml(methodBlock, property, false)); - private static void writeFromXml(JavaClass classBlock, ClientModel model, - ClientModelPropertiesManager propertiesManager, JavaSettings settings, - Consumer addGeneratedAnnotation) { - if (isSuperTypeWithDiscriminator(model)) { - writeSuperTypeFromXml(classBlock, model, propertiesManager, settings, addGeneratedAnnotation); - } else { - writeTerminalTypeFromXml(classBlock, propertiesManager, settings, addGeneratedAnnotation); + // Valid XML should only either have elements or text. + if (propertiesManager.hasXmlElements()) { + propertiesManager.forEachSuperXmlElement(property -> serializeXml(methodBlock, property, true)); + propertiesManager.forEachXmlElement(property -> serializeXml(methodBlock, property, false)); + } else { + propertiesManager.forEachSuperXmlText(property -> serializeXml(methodBlock, property, true)); + propertiesManager.forEachXmlText(property -> serializeXml(methodBlock, property, false)); + } + + methodBlock.methodReturn("xmlWriter.writeEndElement()"); + }); } - } - /** - * Writes a super type's {@code fromXml(XmlReader)} method. - * - * @param classBlock The class having {@code fromXml(XmlReader)} written to it. - * @param model The Autorest representation of the model. - * @param propertiesManager The properties for the model. - * @param settings The Autorest generation settings. - * @param addGeneratedAnnotation Consumer for adding the generated annotation to the class. - */ - private static void writeSuperTypeFromXml(JavaClass classBlock, ClientModel model, - ClientModelPropertiesManager propertiesManager, JavaSettings settings, - Consumer addGeneratedAnnotation) { - // Handling polymorphic fields while determining which subclass, or the class itself, to deserialize handles the - // discriminator type always as a String. This is permissible as the found discriminator is never being used in - // a setter or for setting a field, unlike in the actual deserialization method where it needs to be the same - // type as the field. - ClientModelProperty discriminatorProperty = propertiesManager.getDiscriminatorProperty().getProperty(); - readXmlObject(classBlock, propertiesManager, false, methodBlock -> { - // Assumption for now for XML, only XML properties are used for handling inheritance. - // If this found to be wrong in the future copy the concept of bufferObject and resettable from azure-json - // into azure-xml as bufferElement and resettable. - methodBlock.line("// Get the XML discriminator attribute."); - if (discriminatorProperty.getXmlNamespace() != null) { - methodBlock.line("String discriminatorValue = reader.getStringAttribute(" - + propertiesManager.getXmlNamespaceConstant(discriminatorProperty.getXmlNamespace()) + ", " - + "\"" + discriminatorProperty.getSerializedName() + "\");"); + /** + * Serializes an XML element. + * + * @param methodBlock The method handling serialization. + * @param element The XML element being serialized. + * @param fromSuperType Whether the property is defined in the super type. + */ + private void serializeXml(JavaBlock methodBlock, ClientModelProperty element, boolean fromSuperType) { + IType clientType = element.getClientType(); + IType wireType = element.getWireType(); + String propertyValueGetter; + if (fromSuperType) { + propertyValueGetter = (clientType != wireType) ? wireType.convertFromClientType( + element.getGetterName() + "()") : element.getGetterName() + "()"; } else { - methodBlock.line("String discriminatorValue = reader.getStringAttribute(" - + "\"" + discriminatorProperty.getSerializedName() + "\");"); + propertyValueGetter = "this." + element.getName(); } - methodBlock.line("// Use the discriminator value to determine which subtype should be deserialized."); + // Attempt to determine whether the wire type is simple serialization. + // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. + String xmlSerializationMethodCall = wireType.xmlSerializationMethodCall("xmlWriter", element.getXmlName(), + propertiesManager.getXmlNamespaceConstant(element.getXmlNamespace()), propertyValueGetter, + element.isXmlAttribute(), false, true); + if (xmlSerializationMethodCall != null) { + Consumer serializationLogic = javaBlock -> { + // XML text has special handling. + if (element.isXmlText()) { + javaBlock.line("xmlWriter.writeString(" + propertyValueGetter + ");"); + } else { + javaBlock.line(xmlSerializationMethodCall + ";"); + } + }; - // Add deserialization for the super type itself. - JavaIfBlock ifBlock = null; + // If the property is from a super type and the client type is different from the wire type then a null + // check is required to prevent a NullPointerException when converting the value. + if (fromSuperType && clientType != wireType && clientType.isNullable()) { + methodBlock.ifBlock(propertyValueGetter + " != null", serializationLogic); + } else { + serializationLogic.accept(methodBlock); + } + } else if (wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { + methodBlock.line("xmlWriter.writeXml(" + propertyValueGetter + ");"); + } else if (wireType instanceof IterableType) { + IType elementType = ((IterableType) wireType).getElementType(); + + methodBlock.ifBlock(propertyValueGetter + " != null", ifAction -> { + if (element.isXmlWrapper()) { + String writeStartElement = element.getXmlNamespace() == null + ? "xmlWriter.writeStartElement(\"" + element.getXmlName() + "\");" + : "xmlWriter.writeStartElement(" + propertiesManager.getXmlNamespaceConstant( + element.getXmlNamespace()) + ", \"" + element.getXmlName() + "\");"; + ifAction.line(writeStartElement); + } - // Add deserialization for all child types. - List childTypes = getAllChildTypes(model, new ArrayList<>()); - for (ClientModel childType : childTypes) { - ifBlock = ifOrElseIf(methodBlock, ifBlock, "\"" + childType.getSerializedName() + "\".equals(discriminatorValue)", - ifStatement -> ifStatement.methodReturn(childType.getName() + (isSuperTypeWithDiscriminator(childType) - ? ".fromXmlInternal(reader, finalRootElementName)" - : ".fromXml(reader, finalRootElementName)"))); - } + String xmlWrite = elementType.xmlSerializationMethodCall("xmlWriter", + element.getXmlListElementName(), + propertiesManager.getXmlNamespaceConstant(element.getXmlListElementNamespace()), "element", + false, false, true); + ifAction.line("for (%s element : %s) {", elementType, propertyValueGetter); + ifAction.indent(() -> ifAction.line(xmlWrite + ";")); + ifAction.line("}"); - if (ifBlock == null) { - methodBlock.methodReturn("fromXmlInternal(reader, finalRootElementName)"); + if (element.isXmlWrapper()) { + ifAction.line("xmlWriter.writeEndElement();"); + } + }); + } else if (wireType instanceof MapType) { + // Assumption is that the key type for the Map is a String. This may not always hold true and when that + // becomes reality this will need to be reworked to handle that case. + IType valueType = ((MapType) wireType).getValueType(); + + methodBlock.ifBlock(propertyValueGetter + " != null", ifAction -> { + ifAction.line("xmlWriter.writeStartElement(\"" + element.getXmlName() + "\");"); + + if (valueType instanceof ClassType && ((ClassType) valueType).isSwaggerType()) { + String writeStartElement = (element.getXmlNamespace() != null) + ? "xmlWriter.writeStartElement(" + propertiesManager.getXmlNamespaceConstant( + element.getXmlNamespace()) + ", key);" + : "xmlWriter.writeStartElement(key);"; + + ifAction.line("for (Map.Entry entry : %s.entrySet()) {", valueType, + propertyValueGetter); + ifAction.indent(() -> { + ifAction.line(writeStartElement); + ifAction.line("xmlWriter.writeXml(entry.getValue());"); + ifAction.line("xmlWriter.writeEndElement();"); + }); + ifAction.line("}"); + } else { + String xmlWrite = valueType.xmlSerializationMethodCall("xmlWriter", "entry.getKey()", + propertiesManager.getXmlNamespaceConstant(element.getXmlNamespace()), "entry.getValue()", + false, true, true); + + ifAction.line("for (Map.Entry entry : %s.entrySet()) {", valueType, + propertyValueGetter); + ifAction.indent(() -> ifAction.line(xmlWrite + ";")); + ifAction.line("}"); + } + + ifAction.line("xmlWriter.writeEndElement();"); + }); } else { - ifBlock.elseBlock(elseBlock -> elseBlock.methodReturn("fromXmlInternal(reader, finalRootElementName)")); + // TODO (alzimmer): Resolve this as serialization logic generation needs to handle all cases. + throw new RuntimeException("Unknown wire type " + wireType + " in XML element serialization. " + + "Need to add support for it."); } - }, addGeneratedAnnotation); - - readXmlObject(classBlock, propertiesManager, true, - methodBlock -> writeFromXmlDeserialization(methodBlock, propertiesManager, settings), - addGeneratedAnnotation); - } - - /** - * Adds a static method to the class with the signature that handles reading the XML string into the object type. - *

- * If {@code superTypeReading} is true the method will be package-private and named - * {@code fromXmlWithKnownDiscriminator} instead of being public and named {@code fromXml}. This is done as super - * types use their {@code fromXml} method to determine the discriminator value and pass the reader to the specific - * type being deserialized. The specific type being deserialized may be the super type itself, so it cannot pass to - * {@code fromXml} as this will be a circular call and if the specific type being deserialized is an intermediate - * type (a type having both super and subclasses) it will attempt to perform discriminator validation which has - * already been done. - * - * @param classBlock The class where the {@code fromXml} method is being written. - * @param propertiesManager Properties information about the object being deserialized. - * @param superTypeReading Whether the object reading is for a super type. - * @param deserializationBlock Logic for deserializing the object. - * @param addGeneratedAnnotation Consumer for adding the generated annotation to the class. - */ - private static void readXmlObject(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - boolean superTypeReading, Consumer deserializationBlock, - Consumer addGeneratedAnnotation) { - JavaVisibility visibility = superTypeReading ? JavaVisibility.PackagePrivate : JavaVisibility.Public; - String methodName = superTypeReading ? "fromXmlInternal" : "fromXml"; - - String modelName = propertiesManager.getModel().getName(); - boolean hasRequiredProperties = propertiesManager.hasConstructorArguments(); - boolean isPolymorphic = propertiesManager.getDiscriminatorProperty() != null - && CoreUtils.isNullOrEmpty(propertiesManager.getModel().getDerivedModels()); - - if (!superTypeReading) { - fromXmlJavadoc(classBlock, modelName, false, hasRequiredProperties, isPolymorphic); - addGeneratedAnnotation.accept(classBlock); - classBlock.publicStaticMethod(modelName + " fromXml(XmlReader xmlReader) throws XMLStreamException", - methodBlock -> methodBlock.methodReturn("fromXml(xmlReader, null)")); - - fromXmlJavadoc(classBlock, modelName, true, hasRequiredProperties, isPolymorphic); } - addGeneratedAnnotation.accept(classBlock); - classBlock.staticMethod(visibility, modelName + " " + methodName + "(XmlReader xmlReader, String rootElementName) throws XMLStreamException", methodBlock -> { - // For now, use the basic readObject which will return null if the XmlReader is pointing to JsonToken.NULL. - // - // Support for a default value if null will need to be supported and for objects that get their value - // from a JSON value instead of JSON object or are an array type. - String requiredElementName = propertiesManager.getXmlRootElementName(); - String requiredNamespace = propertiesManager.getXmlRootElementNamespace(); + /** + * Writes a super type's {@code fromXml(XmlReader)} method. + * + * @param classBlock The class having {@code fromXml(XmlReader)} written to it. + */ + private void writeSuperTypeFromXml(JavaClass classBlock) { + // Handling polymorphic fields while determining which subclass, or the class itself, to deserialize handles the + // discriminator type always as a String. This is permissible as the found discriminator is never being used in + // a setter or for setting a field, unlike in the actual deserialization method where it needs to be the same + // type as the field. + ClientModelProperty discriminatorProperty = propertiesManager.getDiscriminatorProperty().getProperty(); + readXmlObject(classBlock, false, methodBlock -> { + // Assumption for now for XML, only XML properties are used for handling inheritance. + // If this found to be wrong in the future copy the concept of bufferObject and resettable from azure-json + // into azure-xml as bufferElement and resettable. + methodBlock.line("// Get the XML discriminator attribute."); + if (discriminatorProperty.getXmlNamespace() != null) { + methodBlock.line("String discriminatorValue = reader.getStringAttribute(" + + propertiesManager.getXmlNamespaceConstant(discriminatorProperty.getXmlNamespace()) + ", " + + "\"" + discriminatorProperty.getSerializedName() + "\");"); + } else { + methodBlock.line("String discriminatorValue = reader.getStringAttribute(" + "\"" + + discriminatorProperty.getSerializedName() + "\");"); + } - methodBlock.line("String finalRootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? " - + "\"" + requiredElementName + "\" : rootElementName;"); - if (requiredNamespace != null) { - methodBlock.line("return xmlReader.readObject(" + propertiesManager.getXmlNamespaceConstant(requiredNamespace) + ", finalRootElementName, reader -> {"); - } else { - methodBlock.line("return xmlReader.readObject(finalRootElementName, reader -> {"); - } + methodBlock.line("// Use the discriminator value to determine which subtype should be deserialized."); - deserializationBlock.accept(methodBlock); + // Add deserialization for the super type itself. + JavaIfBlock ifBlock = null; - methodBlock.line("});"); - }); - } + // Add deserialization for all child types. + List childTypes = getAllChildTypes(model, new ArrayList<>()); + for (ClientModel childType : childTypes) { + ifBlock = ifOrElseIf(methodBlock, ifBlock, + "\"" + childType.getSerializedName() + "\".equals(discriminatorValue)", + ifStatement -> ifStatement.methodReturn( + childType.getName() + (isSuperTypeWithDiscriminator(childType) + ? ".fromXmlInternal(reader, finalRootElementName)" + : ".fromXml(reader, finalRootElementName)"))); + } - private static void fromXmlJavadoc(JavaClass classBlock, String modelName, boolean hasRootElementName, - boolean hasRequiredProperties, boolean isPolymorphic) { - classBlock.javadocComment(javadocComment -> { - javadocComment.description("Reads an instance of " + modelName + " from the XmlReader."); - javadocComment.param("xmlReader", "The XmlReader being read."); - if (hasRootElementName) { - javadocComment.param("rootElementName", "Optional root element name to override the default defined " - + "by the model. Used to support cases where the model can deserialize from different root element " - + "names."); - } - javadocComment.methodReturns("An instance of " + modelName + " if the XmlReader was pointing to an " - + "instance of it, or null if it was pointing to XML null."); - - // TODO (alzimmer): Make the throws statement more descriptive by including the polymorphic - // discriminator property name and the required property names. For now this covers the base functionality. - String throwsStatement = null; - if (hasRequiredProperties && isPolymorphic) { - throwsStatement = "If the deserialized XML object was missing any required properties or the " - + "polymorphic discriminator value is invalid."; - } else if (hasRequiredProperties) { - throwsStatement = "If the deserialized XML object was missing any required properties."; - } else if (isPolymorphic) { - throwsStatement = "If the deserialized XML object has an invalid polymorphic discriminator value."; - } + if (ifBlock == null) { + methodBlock.methodReturn("fromXmlInternal(reader, finalRootElementName)"); + } else { + ifBlock.elseBlock( + elseBlock -> elseBlock.methodReturn("fromXmlInternal(reader, finalRootElementName)")); + } + }); - if (throwsStatement != null) { - javadocComment.methodThrows("IllegalStateException", throwsStatement); + readXmlObject(classBlock, true, this::writeFromXmlDeserialization); + } + + /** + * Adds a static method to the class with the signature that handles reading the XML string into the object + * type. + *

+ * If {@code superTypeReading} is true the method will be package-private and named + * {@code fromXmlWithKnownDiscriminator} instead of being public and named {@code fromXml}. This is done as + * super types use their {@code fromXml} method to determine the discriminator value and pass the reader to the + * specific type being deserialized. The specific type being deserialized may be the super type itself, so it + * cannot pass to {@code fromXml} as this will be a circular call and if the specific type being deserialized is + * an intermediate type (a type having both super and subclasses) it will attempt to perform discriminator + * validation which has already been done. + * + * @param classBlock The class where the {@code fromXml} method is being written. + * @param superTypeReading Whether the object reading is for a super type. + * @param deserializationBlock Logic for deserializing the object. + */ + private void readXmlObject(JavaClass classBlock, boolean superTypeReading, + Consumer deserializationBlock) { + JavaVisibility visibility = superTypeReading ? JavaVisibility.PackagePrivate : JavaVisibility.Public; + String methodName = superTypeReading ? "fromXmlInternal" : "fromXml"; + + if (!superTypeReading) { + fromXmlJavadoc(classBlock, false); + addGeneratedAnnotation.accept(classBlock); + classBlock.publicStaticMethod( + model.getName() + " fromXml(XmlReader xmlReader) throws XMLStreamException", + methodBlock -> methodBlock.methodReturn("fromXml(xmlReader, null)")); + + fromXmlJavadoc(classBlock, true); } - javadocComment.methodThrows("XMLStreamException", "If an error occurs while reading the " + modelName + "."); - }); - } + addGeneratedAnnotation.accept(classBlock); + classBlock.staticMethod(visibility, model.getName() + " " + methodName + + "(XmlReader xmlReader, String rootElementName) throws XMLStreamException", methodBlock -> { + // For now, use the basic readObject which will return null if the XmlReader is pointing to JsonToken.NULL. + // + // Support for a default value if null will need to be supported and for objects that get their value + // from a JSON value instead of JSON object or are an array type. + String requiredElementName = propertiesManager.getXmlRootElementName(); + String requiredNamespace = propertiesManager.getXmlRootElementNamespace(); + + methodBlock.line("String finalRootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? " + "\"" + + requiredElementName + "\" : rootElementName;"); + if (requiredNamespace != null) { + methodBlock.line( + "return xmlReader.readObject(" + propertiesManager.getXmlNamespaceConstant(requiredNamespace) + + ", finalRootElementName, reader -> {"); + } else { + methodBlock.line("return xmlReader.readObject(finalRootElementName, reader -> {"); + } - /** - * Writes a terminal type's {@code fromXml(XmlReader)} method. - *

- * A terminal type is either a type without polymorphism or is the terminal type in a polymorphic hierarchy. - * - * @param classBlock The class having {@code fromXml(XmlReader)} written to it. - * @param propertiesManager The properties for the model. - * @param settings The Autorest generation settings. - * @param addGeneratedAnnotation Consumer for adding the generated annotation to the class. - */ - private static void writeTerminalTypeFromXml(JavaClass classBlock, ClientModelPropertiesManager propertiesManager, - JavaSettings settings, Consumer addGeneratedAnnotation) { - readXmlObject(classBlock, propertiesManager, false, - methodBlock -> writeFromXmlDeserialization(methodBlock, propertiesManager, settings), - addGeneratedAnnotation); - } + deserializationBlock.accept(methodBlock); - private static void writeFromXmlDeserialization(JavaBlock methodBlock, - ClientModelPropertiesManager propertiesManager, JavaSettings settings) { + methodBlock.line("});"); + }); + } - // Add the deserialization logic. - methodBlock.indent(() -> { - // Initialize local variables to track what has been deserialized. - initializeLocalVariables(methodBlock, propertiesManager, true, settings); + private void fromXmlJavadoc(JavaClass classBlock, boolean hasRootElementName) { + classBlock.javadocComment(javadocComment -> { + javadocComment.description("Reads an instance of " + model.getName() + " from the XmlReader."); + javadocComment.param("xmlReader", "The XmlReader being read."); + if (hasRootElementName) { + javadocComment.param("rootElementName", + "Optional root element name to override the default defined by the model. Used to support " + + "cases where the model can deserialize from different root element names."); + } + javadocComment.methodReturns( + "An instance of " + model.getName() + " if the XmlReader was pointing to an " + + "instance of it, or null if it was pointing to XML null."); + + // TODO (alzimmer): Make the throws statement more descriptive by including the polymorphic + // discriminator property name and the required property names. For now this covers the base functionality. + String throwsStatement = null; + if (propertiesManager.hasConstructorArguments() && model.isPolymorphic()) { + throwsStatement = "If the deserialized XML object was missing any required properties or the " + + "polymorphic discriminator value is invalid."; + } else if (propertiesManager.hasConstructorArguments()) { + throwsStatement = "If the deserialized XML object was missing any required properties."; + } else if (model.isPolymorphic()) { + throwsStatement = "If the deserialized XML object has an invalid polymorphic discriminator value."; + } - // Assumption for XML is polymorphic discriminators are attributes. - if (propertiesManager.getDiscriminatorProperty() != null) { - deserializeXmlAttribute(methodBlock, propertiesManager.getDiscriminatorProperty().getProperty(), - propertiesManager, false); - } + if (throwsStatement != null) { + javadocComment.methodThrows("IllegalStateException", throwsStatement); + } - // Read the XML attribute properties first. - propertiesManager.forEachSuperXmlAttribute(attribute -> deserializeXmlAttribute(methodBlock, attribute, - propertiesManager, true)); - propertiesManager.forEachXmlAttribute(attribute -> deserializeXmlAttribute(methodBlock, attribute, - propertiesManager, false)); + javadocComment.methodThrows("XMLStreamException", + "If an error occurs while reading the " + model.getName() + "."); + }); + } - // Read the XML text next. - propertiesManager.forEachSuperXmlText(text -> deserializeXmlText(methodBlock, text, propertiesManager, true)); - propertiesManager.forEachXmlText(text -> deserializeXmlText(methodBlock, text, propertiesManager, false)); + /** + * Writes a terminal type's {@code fromXml(XmlReader)} method. + *

+ * A terminal type is either a type without polymorphism or is the terminal type in a polymorphic hierarchy. + * + * @param classBlock The class having {@code fromXml(XmlReader)} written to it. + */ + private void writeTerminalTypeFromXml(JavaClass classBlock) { + readXmlObject(classBlock, false, this::writeFromXmlDeserialization); + } - // Model didn't have any XML elements, return early. - String fieldNameVariableName = propertiesManager.getXmlReaderNameVariableName(); - if (!propertiesManager.hasXmlElements()) { - // If the model was attributes only a simplified read loop is needed to ensure the end element token - // is reached. - if (!propertiesManager.hasXmlTexts()) { - methodBlock.block("while (reader.nextElement() != XmlToken.END_ELEMENT)", - whileBlock -> whileBlock.line("reader.skipElement();")); - } - return; - } + private void writeFromXmlDeserialization(JavaBlock methodBlock) { + // Add the deserialization logic. + methodBlock.indent(() -> { + // Initialize local variables to track what has been deserialized. + initializeLocalVariables(methodBlock, true); - // Add the outermost while loop to read the JSON object. - addReaderWhileLoop(methodBlock, true, fieldNameVariableName, true, whileBlock -> { - JavaIfBlock ifBlock = null; + // Assumption for XML is polymorphic discriminators are attributes. + if (propertiesManager.getDiscriminatorProperty() != null) { + deserializeXmlAttribute(methodBlock, propertiesManager.getDiscriminatorProperty().getProperty(), + false); + } - if (propertiesManager.getDiscriminatorProperty() != null - && !propertiesManager.getDiscriminatorProperty().getProperty().isXmlAttribute()) { - ClientModelProperty discriminatorProperty = propertiesManager.getDiscriminatorProperty() - .getProperty(); - String ifStatement = String.format("\"%s\".equals(%s)", propertiesManager.getExpectedDiscriminator(), - fieldNameVariableName); - - ifBlock = methodBlock.ifBlock(ifStatement, ifAction -> { - ifAction.line("String %s = reader.getStringElement().getLocalPart();", discriminatorProperty.getName()); - String ifStatement2 = String.format("!%s.equals(%s)", discriminatorProperty.getDefaultValue(), - discriminatorProperty.getName()); - ifAction.ifBlock(ifStatement2, ifAction2 -> ifAction2.line( - "throw new IllegalStateException(\"'%s' was expected to be non-null and equal to '\"%s\"'. " - + "The found '%s' was '\" + %s + \"'.\");", - discriminatorProperty.getSerializedName(), propertiesManager.getExpectedDiscriminator(), - discriminatorProperty.getSerializedName(), discriminatorProperty.getName())); - }); + // Read the XML attribute properties first. + propertiesManager.forEachSuperXmlAttribute( + attribute -> deserializeXmlAttribute(methodBlock, attribute, true)); + propertiesManager.forEachXmlAttribute( + attribute -> deserializeXmlAttribute(methodBlock, attribute, false)); + + // Read the XML text next. + propertiesManager.forEachSuperXmlText(text -> deserializeXmlText(methodBlock, text, true)); + propertiesManager.forEachXmlText(text -> deserializeXmlText(methodBlock, text, false)); + + // Model didn't have any XML elements, return early. + String fieldNameVariableName = propertiesManager.getXmlReaderNameVariableName(); + if (!propertiesManager.hasXmlElements()) { + // If the model was attributes only a simplified read loop is needed to ensure the end element token + // is reached. + if (!propertiesManager.hasXmlTexts()) { + methodBlock.block("while (reader.nextElement() != XmlToken.END_ELEMENT)", + whileBlock -> whileBlock.line("reader.skipElement();")); + } + return; } - // Loop over all properties and generate their deserialization handling. - AtomicReference ifBlockReference = new AtomicReference<>(ifBlock); - propertiesManager.forEachSuperXmlElement(element -> handleXmlPropertyDeserialization(element, - whileBlock, ifBlockReference, fieldNameVariableName, propertiesManager, true, settings)); - propertiesManager.forEachXmlElement(element -> handleXmlPropertyDeserialization(element, whileBlock, - ifBlockReference, fieldNameVariableName, propertiesManager, false, settings)); + // Add the outermost while loop to read the JSON object. + addReaderWhileLoop(methodBlock, true, true, whileBlock -> { + JavaIfBlock ifBlock = null; + + if (propertiesManager.getDiscriminatorProperty() != null + && !propertiesManager.getDiscriminatorProperty().getProperty().isXmlAttribute()) { + ClientModelProperty discriminatorProperty = propertiesManager.getDiscriminatorProperty() + .getProperty(); + String ifStatement = String.format("\"%s\".equals(%s)", + propertiesManager.getExpectedDiscriminator(), fieldNameVariableName); + + ifBlock = methodBlock.ifBlock(ifStatement, ifAction -> { + ifAction.line("String %s = reader.getStringElement().getLocalPart();", + discriminatorProperty.getName()); + String ifStatement2 = String.format("!%s.equals(%s)", + discriminatorProperty.getDefaultValue(), discriminatorProperty.getName()); + ifAction.ifBlock(ifStatement2, ifAction2 -> ifAction2.line( + "throw new IllegalStateException(\"'%s' was expected to be non-null and equal to '\"%s\"'. " + + "The found '%s' was '\" + %s + \"'.\");", + discriminatorProperty.getSerializedName(), propertiesManager.getExpectedDiscriminator(), + discriminatorProperty.getSerializedName(), discriminatorProperty.getName())); + }); + } - ifBlock = ifBlockReference.get(); + // Loop over all properties and generate their deserialization handling. + AtomicReference ifBlockReference = new AtomicReference<>(ifBlock); + propertiesManager.forEachSuperXmlElement( + element -> handleXmlPropertyDeserialization(element, whileBlock, ifBlockReference, true)); + propertiesManager.forEachXmlElement( + element -> handleXmlPropertyDeserialization(element, whileBlock, ifBlockReference, false)); - // All properties have been checked for, add an else block that will either ignore unknown properties - // or add them into an additional properties bag. - ClientModelProperty additionalProperty = getAdditionalPropertiesPropertyInModelOrFromSuper(propertiesManager); - handleUnknownXmlFieldDeserialization(whileBlock, ifBlock, additionalProperty, - propertiesManager.getXmlReaderNameVariableName()); + ifBlock = ifBlockReference.get(); + + // All properties have been checked for, add an else block that will either ignore unknown + // properties or add them into an additional properties bag. + handleUnknownXmlFieldDeserialization(whileBlock, ifBlock); + }); }); - }); - // Add the validation and return logic. - handleReadReturn(methodBlock, propertiesManager.getModel().getName(), propertiesManager, settings); - } + // Add the validation and return logic. + handleReadReturn(methodBlock); + } - private static void deserializeXmlAttribute(JavaBlock methodBlock, ClientModelProperty attribute, - ClientModelPropertiesManager propertiesManager, boolean fromSuper) { - String xmlAttributeDeserialization = getSimpleXmlDeserialization(attribute.getWireType(), "reader", null, - attribute.getXmlName(), propertiesManager.getXmlNamespaceConstant(attribute.getXmlNamespace()), true); + private void deserializeXmlAttribute(JavaBlock methodBlock, ClientModelProperty attribute, boolean fromSuper) { + String xmlAttributeDeserialization = getSimpleXmlDeserialization(attribute.getWireType(), null, + attribute.getXmlName(), propertiesManager.getXmlNamespaceConstant(attribute.getXmlNamespace()), true); - if (attribute.isPolymorphicDiscriminator() - && CoreUtils.isNullOrEmpty(propertiesManager.getModel().getDerivedModels())) { - // Only validate the discriminator if the model has no derived models. - // Super types will deserialize as themselves if the discriminator doesn't match what's expected. - methodBlock.line("String discriminatorValue = " + xmlAttributeDeserialization + ";"); - String ifStatement = String.format("!%s.equals(discriminatorValue)", attribute.getDefaultValue()); - methodBlock.ifBlock(ifStatement, ifAction2 -> ifAction2.line( - "throw new IllegalStateException(\"'%s' was expected to be non-null and equal to '%s'. " - + "The found '%s' was '\" + discriminatorValue + \"'.\");", - attribute.getSerializedName(), propertiesManager.getExpectedDiscriminator(), - attribute.getSerializedName())); + if (attribute.isPolymorphicDiscriminator() && CoreUtils.isNullOrEmpty(model.getDerivedModels())) { + // Only validate the discriminator if the model has no derived models. + // Super types will deserialize as themselves if the discriminator doesn't match what's expected. + methodBlock.line("String discriminatorValue = " + xmlAttributeDeserialization + ";"); + String ifStatement = String.format("!%s.equals(discriminatorValue)", attribute.getDefaultValue()); + methodBlock.ifBlock(ifStatement, ifAction2 -> ifAction2.line( + "throw new IllegalStateException(\"'%s' was expected to be non-null and equal to '%s'. " + + "The found '%s' was '\" + discriminatorValue + \"'.\");", attribute.getSerializedName(), + propertiesManager.getExpectedDiscriminator(), attribute.getSerializedName())); - xmlAttributeDeserialization = "discriminatorValue"; - } + xmlAttributeDeserialization = "discriminatorValue"; + } - if (propertiesManager.hasConstructorArguments()) { - methodBlock.line("%s %s = %s;", attribute.getClientType(), attribute.getName(), xmlAttributeDeserialization); - } else { - handleSettingDeserializedValue(methodBlock, propertiesManager.getDeserializedModelName(), - propertiesManager.getModel(), attribute, xmlAttributeDeserialization, fromSuper, false); + if (propertiesManager.hasConstructorArguments()) { + methodBlock.line(attribute.getClientType() + " " + attribute.getName() + " = " + xmlAttributeDeserialization + ";"); + } else { + handleSettingDeserializedValue(methodBlock, attribute, xmlAttributeDeserialization, fromSuper); + } } - } - private static void deserializeXmlText(JavaBlock methodBlock, ClientModelProperty text, - ClientModelPropertiesManager propertiesManager, boolean fromSuper) { - String xmlTextDeserialization = getSimpleXmlDeserialization(text.getWireType(), "reader", null, null, null, false); - if (propertiesManager.hasConstructorArguments()) { - methodBlock.line(text.getClientType() + " " + text.getName() + " = " + xmlTextDeserialization + ";"); - } else { - handleSettingDeserializedValue(methodBlock, propertiesManager.getDeserializedModelName(), - propertiesManager.getModel(), text, xmlTextDeserialization, fromSuper, false); + private void deserializeXmlText(JavaBlock methodBlock, ClientModelProperty text, boolean fromSuper) { + String xmlTextDeserialization = getSimpleXmlDeserialization(text.getWireType(), null, null, null, + false); + if (propertiesManager.hasConstructorArguments()) { + methodBlock.line(text.getClientType() + " " + text.getName() + " = " + xmlTextDeserialization + ";"); + } else { + handleSettingDeserializedValue(methodBlock, text, xmlTextDeserialization, fromSuper); + } } - } - private static void handleXmlPropertyDeserialization(ClientModelProperty property, JavaBlock methodBlock, - AtomicReference ifBlockReference, String fieldNameVariableName, - ClientModelPropertiesManager propertiesManager, boolean fromSuper, JavaSettings settings) { - // Property will be handled later by flattened deserialization. - // XML should never have flattening. - if (property.getNeedsFlatten()) { - return; - } + private void handleXmlPropertyDeserialization(ClientModelProperty property, JavaBlock methodBlock, + AtomicReference ifBlockReference, boolean fromSuper) { + // Property will be handled later by flattened deserialization. + // XML should never have flattening. + if (property.getNeedsFlatten()) { + return; + } - JavaIfBlock ifBlock = ifBlockReference.get(); - ifBlock = handleXmlPropertyDeserialization(property, methodBlock, ifBlock, fieldNameVariableName, - propertiesManager, fromSuper, settings); + JavaIfBlock ifBlock = ifBlockReference.get(); + ifBlockReference.set(handleXmlPropertyDeserialization(property, methodBlock, ifBlock, fromSuper)); + } - ifBlockReference.set(ifBlock); - } + private JavaIfBlock handleXmlPropertyDeserialization(ClientModelProperty property, JavaBlock methodBlock, + JavaIfBlock ifBlock, boolean fromSuper) { + String xmlElementName = (property.getClientType() instanceof IterableType && !property.isXmlWrapper()) + ? property.getXmlListElementName() + : property.getXmlName(); + String xmlNamespace = propertiesManager.getXmlNamespaceConstant(property.getXmlNamespace()); - private static JavaIfBlock handleXmlPropertyDeserialization(ClientModelProperty property, JavaBlock methodBlock, - JavaIfBlock ifBlock, String fieldNameVariableName, ClientModelPropertiesManager propertiesManager, - boolean fromSuper, JavaSettings settings) { - String xmlElementName = (property.getClientType() instanceof IterableType && !property.isXmlWrapper()) - ? property.getXmlListElementName() : property.getXmlName(); - String xmlNamespace = propertiesManager.getXmlNamespaceConstant(property.getXmlNamespace()); + if (CoreUtils.isNullOrEmpty(xmlElementName)) { + return ifBlock; + } - if (CoreUtils.isNullOrEmpty(xmlElementName)) { - return ifBlock; + String condition = getXmlNameConditional(xmlElementName, xmlNamespace, + propertiesManager.getXmlReaderNameVariableName(), true); + return ifOrElseIf(methodBlock, ifBlock, condition, + deserializationBlock -> generateXmlDeserializationLogic(deserializationBlock, property, fromSuper)); } - String condition = getXmlNameConditional(xmlElementName, xmlNamespace, fieldNameVariableName, true); - return ifOrElseIf(methodBlock, ifBlock, condition, - deserializationBlock -> generateXmlDeserializationLogic(deserializationBlock, property, propertiesManager, - fromSuper, settings)); - } - - private static void generateXmlDeserializationLogic(JavaBlock deserializationBlock, ClientModelProperty property, - ClientModelPropertiesManager propertiesManager, boolean fromSuper, JavaSettings settings) { - IType wireType = property.getWireType(); + private void generateXmlDeserializationLogic(JavaBlock deserializationBlock, ClientModelProperty property, + boolean fromSuper) { + IType wireType = property.getWireType(); - // Attempt to determine whether the wire type is simple deserialization. - // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. - String simpleDeserialization = getSimpleXmlDeserialization(wireType, "reader", property.getXmlName(), null, - null, false); - if (simpleDeserialization != null) { - if (propertiesManager.hasConstructorArguments()) { - deserializationBlock.line(property.getName() + " = " + simpleDeserialization + ";"); - } else { - handleSettingDeserializedValue(deserializationBlock, propertiesManager.getDeserializedModelName(), - propertiesManager.getModel(), property, simpleDeserialization, fromSuper, false); - } - } else if (wireType instanceof IterableType) { - IType elementType = ((IterableType) wireType).getElementType(); - boolean sameNames = Objects.equals(property.getXmlName(), property.getXmlListElementName()); - String elementDeserialization = getSimpleXmlDeserialization(elementType, "reader", - sameNames ? property.getXmlName() : property.getXmlListElementName(), null, null, false); - String fieldAccess; - if (propertiesManager.hasConstructorArguments()) { - // Cases with constructor arguments will have a local variable based on the name of the property. - fieldAccess = property.getName(); - } else if (fromSuper) { - // Cases where the property is from the super type will need to access the getter. - fieldAccess = propertiesManager.getDeserializedModelName() + "." + property.getGetterName() + "()"; - } else { - // Otherwise access the property directly. - fieldAccess = propertiesManager.getDeserializedModelName() + "." + property.getName(); - } + // Attempt to determine whether the wire type is simple deserialization. + // This is primitives, boxed primitives, a small set of string based models, and other ClientModels. + String simpleDeserialization = getSimpleXmlDeserialization(wireType, property.getXmlName(), null, + null, false); + if (simpleDeserialization != null) { + if (propertiesManager.hasConstructorArguments()) { + deserializationBlock.line(property.getName() + " = " + simpleDeserialization + ";"); + } else { + handleSettingDeserializedValue(deserializationBlock, property, simpleDeserialization, fromSuper); + } + } else if (wireType instanceof IterableType) { + IType elementType = ((IterableType) wireType).getElementType(); + boolean sameNames = Objects.equals(property.getXmlName(), property.getXmlListElementName()); + String elementDeserialization = getSimpleXmlDeserialization(elementType, + sameNames ? property.getXmlName() : property.getXmlListElementName(), null, null, false); + String fieldAccess; + if (propertiesManager.hasConstructorArguments()) { + // Cases with constructor arguments will have a local variable based on the name of the property. + fieldAccess = property.getName(); + } else if (fromSuper) { + // Cases where the property is from the super type will need to access the getter. + fieldAccess = propertiesManager.getDeserializedModelName() + "." + property.getGetterName() + "()"; + } else { + // Otherwise access the property directly. + fieldAccess = propertiesManager.getDeserializedModelName() + "." + property.getName(); + } - if (!property.isXmlWrapper()) { - deserializationBlock.line(fieldAccess + ".add(" + elementDeserialization + ");"); - } else { - deserializationBlock.block("while (reader.nextElement() != XmlToken.END_ELEMENT)", whileBlock -> { - whileBlock.line("elementName = reader.getElementName();"); - String condition = getXmlNameConditional(property.getXmlListElementName(), - propertiesManager.getXmlNamespaceConstant(property.getXmlListElementNamespace()), "elementName", - true); - whileBlock.ifBlock(condition, ifBlock -> { + if (!property.isXmlWrapper()) { + deserializationBlock.line(fieldAccess + ".add(" + elementDeserialization + ");"); + } else { + deserializationBlock.block("while (reader.nextElement() != XmlToken.END_ELEMENT)", whileBlock -> { + whileBlock.line("elementName = reader.getElementName();"); + String condition = getXmlNameConditional(property.getXmlListElementName(), + propertiesManager.getXmlNamespaceConstant(property.getXmlListElementNamespace()), + "elementName", true); + whileBlock.ifBlock(condition, ifBlock -> { // TODO (alzimmer): Handle nested container types when needed. ifBlock.ifBlock(fieldAccess + " == null", ifStatement -> { if (fromSuper) { - ifStatement.line(propertiesManager.getDeserializedModelName() + "." + property.getSetterName() - + "(new ArrayList<>());"); + ifStatement.line( + propertiesManager.getDeserializedModelName() + "." + property.getSetterName() + + "(new ArrayList<>());"); } else { ifStatement.line(fieldAccess + " = new ArrayList<>();"); } }); ifBlock.line(fieldAccess + ".add(" + elementDeserialization + ");"); - }) - .elseBlock(elseBlock -> elseBlock.line("reader.skipElement();")); + }).elseBlock(elseBlock -> elseBlock.line("reader.skipElement();")); + }); + } + } else if (wireType instanceof MapType) { + IType valueType = ((MapType) wireType).getValueType(); + String fieldAccess = propertiesManager.hasConstructorArguments() + ? property.getName() + : propertiesManager.getDeserializedModelName() + "." + property.getName(); + + String valueDeserialization = getSimpleXmlDeserialization(valueType, property.getXmlName(), + null, null, false); + deserializationBlock.block("while (reader.nextElement() != XmlToken.END_ELEMENT)", whileBlock -> { + // TODO (alzimmer): Handle nested container types when needed. + // Assumption is that the key type for the Map is a String. This may not always hold true and when that + // becomes reality this will need to be reworked to handle that case. + whileBlock.ifBlock(fieldAccess + " == null", + ifStatement -> ifStatement.line(fieldAccess + " = new LinkedHashMap<>();")); + + whileBlock.line( + fieldAccess + ".put(reader.getElementName().getLocalPart(), " + valueDeserialization + ");"); }); + } else { + // TODO (alzimmer): Resolve this as deserialization logic generation needs to handle all cases. + throw new RuntimeException("Unknown wire type " + wireType + ". Need to add support for it."); } - } else if (wireType instanceof MapType) { - IType valueType = ((MapType) wireType).getValueType(); - String fieldAccess = propertiesManager.hasConstructorArguments() - ? property.getName() - : propertiesManager.getDeserializedModelName() + "." + property.getName(); - - String valueDeserialization = getSimpleXmlDeserialization(valueType, "reader", property.getXmlName(), null, - null, false); - deserializationBlock.block("while (reader.nextElement() != XmlToken.END_ELEMENT)", whileBlock -> { - // TODO (alzimmer): Handle nested container types when needed. - // Assumption is that the key type for the Map is a String. This may not always hold true and when that - // becomes reality this will need to be reworked to handle that case. - whileBlock.ifBlock(fieldAccess + " == null", - ifStatement -> ifStatement.line(fieldAccess + " = new LinkedHashMap<>();")); - - whileBlock.line(fieldAccess + ".put(reader.getElementName().getLocalPart(), " + valueDeserialization + ");"); - }); - } else { - // TODO (alzimmer): Resolve this as deserialization logic generation needs to handle all cases. - throw new RuntimeException("Unknown wire type " + wireType + ". Need to add support for it."); - } - // If the property was required, mark it as found. - if (includePropertyInConstructor(property, settings) && !settings.isDisableRequiredJsonAnnotation()) { - deserializationBlock.line(property.getName() + "Found = true;"); + // If the property was required, mark it as found. + if (includePropertyInConstructor(property, settings) && !settings.isDisableRequiredJsonAnnotation()) { + deserializationBlock.line(property.getName() + "Found = true;"); + } } - } - private static void handleUnknownXmlFieldDeserialization(JavaBlock methodBlock, JavaIfBlock ifBlock, - ClientModelProperty additionalProperties, String fieldNameVariableName) { - Consumer unknownFieldConsumer = javaBlock -> { - if (additionalProperties != null) { - javaBlock.ifBlock(additionalProperties.getName() + " == null", - ifAction -> ifAction.line(additionalProperties.getName() + " = new LinkedHashMap<>();")); - javaBlock.line(); - - // Assumption, additional properties is a Map of String-Object - IType valueType = ((MapType) additionalProperties.getWireType()).getValueType(); - if (valueType == ClassType.OBJECT) { - // String fieldName should be a local variable accessible in this spot of code. - javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + ", reader.readUntyped());"); + private void handleUnknownXmlFieldDeserialization(JavaBlock methodBlock, JavaIfBlock ifBlock) { + ClientModelProperty additionalProperties = getAdditionalPropertiesPropertyInModelOrFromSuper(); + String fieldNameVariableName = propertiesManager.getXmlReaderNameVariableName(); + Consumer unknownFieldConsumer = javaBlock -> { + if (additionalProperties != null) { + javaBlock.ifBlock(additionalProperties.getName() + " == null", + ifAction -> ifAction.line(additionalProperties.getName() + " = new LinkedHashMap<>();")); + javaBlock.line(); + + // Assumption, additional properties is a Map of String-Object + IType valueType = ((MapType) additionalProperties.getWireType()).getValueType(); + if (valueType == ClassType.OBJECT) { + // String fieldName should be a local variable accessible in this spot of code. + javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + + ", reader.readUntyped());"); + } else { + // Another assumption, the additional properties value type is simple. + javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + ", " + + getSimpleXmlDeserialization(valueType, null, null, null, false) + ");"); + } } else { - // Another assumption, the additional properties value type is simple. - javaBlock.line(additionalProperties.getName() + ".put(" + fieldNameVariableName + ", " - + getSimpleXmlDeserialization(valueType, "reader", null, null, null, false) + ");"); + javaBlock.line("reader.skipElement();"); } - } else { - javaBlock.line("reader.skipElement();"); - } - }; - - if (ifBlock == null) { - unknownFieldConsumer.accept(methodBlock); - } else { - ifBlock.elseBlock(unknownFieldConsumer); - } - } - - private static String getSimpleXmlDeserialization(IType wireType, String readerName, String elementName, - String attributeName, String attributeNamespace, boolean namespaceIsConstant) { - if (wireType instanceof ClassType && ((ClassType) wireType).isSwaggerType()) { - return CoreUtils.isNullOrEmpty(elementName) - ? wireType + ".fromXml(" + readerName + ")" - : wireType + ".fromXml(" + readerName + ", \"" + elementName + "\")"; - } else { - return wireType.xmlDeserializationMethod(readerName, attributeName, attributeNamespace, namespaceIsConstant); - } - } + }; - private static List getClientModelPropertiesInJsonTree( - JsonFlattenedPropertiesTree tree) { - if (tree.getProperty() != null) { - // Terminal node only contains a property. - return Collections.singletonList(tree.getProperty()); - } else { - List treeProperties = new ArrayList<>(); - for (JsonFlattenedPropertiesTree childNode : tree.getChildrenNodes().values()) { - treeProperties.addAll(getClientModelPropertiesInJsonTree(childNode)); + if (ifBlock == null) { + unknownFieldConsumer.accept(methodBlock); + } else { + ifBlock.elseBlock(unknownFieldConsumer); } - - return treeProperties; } - } - private static String getXmlNameConditional(String localPart, String namespace, String elementName, - boolean namespaceIsConstant) { - String condition = "\"" + localPart + "\".equals(" + elementName + ".getLocalPart())"; - if (!CoreUtils.isNullOrEmpty(namespace)) { - if (namespaceIsConstant) { - condition += " && " + namespace + ".equals(" + elementName + ".getNamespaceURI())"; + private static List getClientModelPropertiesInJsonTree( + JsonFlattenedPropertiesTree tree) { + if (tree.getProperty() != null) { + // Terminal node only contains a property. + return Collections.singletonList(tree.getProperty()); } else { - condition += " && \"" + namespace + "\".equals(" + elementName + ".getNamespaceURI())"; + List treeProperties = new ArrayList<>(); + for (JsonFlattenedPropertiesTree childNode : tree.getChildrenNodes().values()) { + treeProperties.addAll(getClientModelPropertiesInJsonTree(childNode)); + } + + return treeProperties; } } - - return condition; } } diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/resources/pom.xml b/packages/http-client-java/generator/http-client-generator-core/src/main/resources/pom.xml index b26df1144d..b49485a572 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/resources/pom.xml +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/resources/pom.xml @@ -60,11 +60,6 @@ - - org.apache.maven.plugins - maven-assembly-plugin - 3.7.1 - org.apache.maven.plugins maven-compiler-plugin diff --git a/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/FluentStreamStyleSerializationModelTemplate.java b/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/FluentStreamStyleSerializationModelTemplate.java index 13b6181b1d..53ffb37eee 100644 --- a/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/FluentStreamStyleSerializationModelTemplate.java +++ b/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/FluentStreamStyleSerializationModelTemplate.java @@ -32,7 +32,7 @@ protected boolean parentModelHasValidate(String parentModelName) { } @Override - protected boolean isManagementErrorSubclass(ClientModel model, JavaSettings settings) { + protected boolean isManagementErrorSubclass(ClientModel model) { if (CoreUtils.isNullOrEmpty(model.getParentModelName())) { return false; } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/AbstractModel.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/AbstractModel.java index a1c9c9c752..6128bdba44 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/AbstractModel.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/AbstractModel.java @@ -21,7 +21,7 @@ public class AbstractModel implements JsonSerializable { * Discriminator property for AbstractModel. */ @Generated - String kind; + private String kind = "AbstractModel"; /* * The name property. @@ -37,7 +37,6 @@ public class AbstractModel implements JsonSerializable { @Generated protected AbstractModel(String name) { this.name = name; - this.kind = "AbstractModel"; } /** @@ -67,13 +66,9 @@ public String getName() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStringField("name", this.name); jsonWriter.writeStringField("kind", this.kind); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/RealModel.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/RealModel.java index 891e6fa2bd..93611ab134 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/RealModel.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/_specs_/azure/clientgenerator/core/access/implementation/models/RealModel.java @@ -16,6 +16,12 @@ */ @Immutable public final class RealModel extends AbstractModel { + /* + * Discriminator property for AbstractModel. + */ + @Generated + private String kind = "real"; + /** * Creates an instance of RealModel class. * @@ -24,7 +30,17 @@ public final class RealModel extends AbstractModel { @Generated private RealModel(String name) { super(name); - this.kind = "real"; + } + + /** + * Get the kind property: Discriminator property for AbstractModel. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -34,7 +50,8 @@ private RealModel(String name) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeStringField("name", getName()); + jsonWriter.writeStringField("kind", this.kind); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Dog.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Dog.java index 86e782823c..0fc8c725a0 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Dog.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Dog.java @@ -24,7 +24,7 @@ public class Dog { */ @JsonTypeId @JsonProperty(value = "kind", required = true) - DogKind kind; + private DogKind kind = DogKind.fromString("Dog"); /* * Weight of the dog @@ -36,7 +36,6 @@ public class Dog { * Creates an instance of Dog class. */ public Dog() { - this.kind = DogKind.fromString("Dog"); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Golden.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Golden.java index 077195d193..79f6822549 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Golden.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/armresourceprovider/models/Golden.java @@ -5,6 +5,8 @@ package com.cadl.armresourceprovider.models; import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeId; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; @@ -15,11 +17,27 @@ @JsonTypeName("golden") @Fluent public final class Golden extends Dog { + /* + * discriminator property + */ + @JsonTypeId + @JsonProperty(value = "kind", required = true) + private DogKind kind = DogKind.GOLDEN; + /** * Creates an instance of Golden class. */ public Golden() { - this.kind = DogKind.GOLDEN; + } + + /** + * Get the kind property: discriminator property. + * + * @return the kind value. + */ + @Override + public DogKind kind() { + return this.kind; } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/BytesData.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/BytesData.java index bbe0c7965c..c67dcc97b4 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/BytesData.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/BytesData.java @@ -17,6 +17,12 @@ */ @Immutable public final class BytesData extends Data { + /* + * The @data.kind property. + */ + @Generated + private String type = "bytes"; + /* * Data as {@code byte[]} */ @@ -31,7 +37,17 @@ public final class BytesData extends Data { @Generated private BytesData(byte[] dataAsBytes) { this.dataAsBytes = dataAsBytes; - this.type = "bytes"; + } + + /** + * Get the type property: The @data.kind property. + * + * @return the type value. + */ + @Generated + @Override + public String getType() { + return this.type; } /** @@ -51,8 +67,8 @@ public byte[] getDataAsBytes() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); jsonWriter.writeBinaryField("data_bytes", this.dataAsBytes); + jsonWriter.writeStringField("@data.kind", this.type); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/Data.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/Data.java index e2b92b1591..34da609016 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/Data.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/naming/models/Data.java @@ -22,14 +22,13 @@ public class Data implements JsonSerializable { * The @data.kind property. */ @Generated - String type; + private String type = "Data"; /** * Creates an instance of Data class. */ @Generated protected Data() { - this.type = "Data"; } /** @@ -49,12 +48,8 @@ public String getType() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStringField("@data.kind", this.type); + return jsonWriter.writeEndObject(); } /** @@ -99,7 +94,9 @@ static Data fromJsonKnownDiscriminator(JsonReader jsonReader) throws IOException String fieldName = reader.getFieldName(); reader.nextToken(); - if (!Data.fromJsonShared(reader, fieldName, deserializedData)) { + if ("@data.kind".equals(fieldName)) { + deserializedData.type = reader.getString(); + } else { reader.skipChildren(); } } @@ -107,13 +104,4 @@ static Data fromJsonKnownDiscriminator(JsonReader jsonReader) throws IOException return deserializedData; }); } - - @Generated - static boolean fromJsonShared(JsonReader reader, String fieldName, Data deserializedData) throws IOException { - if ("@data.kind".equals(fieldName)) { - deserializedData.type = reader.getString(); - return true; - } - return false; - } } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Fish.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Fish.java index 86f0597e4e..d555458b52 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Fish.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Fish.java @@ -24,7 +24,7 @@ public class Fish implements JsonSerializable { * Discriminator property for Fish. */ @Generated - String kind; + private String kind = "Fish"; /* * The id property. @@ -104,7 +104,6 @@ public void setColor(Fish model, String color) { */ @Generated public Fish() { - this.kind = "Fish"; } /** @@ -263,7 +262,17 @@ static Fish fromJsonKnownDiscriminator(JsonReader jsonReader) throws IOException String fieldName = reader.getFieldName(); reader.nextToken(); - if (!Fish.fromJsonShared(reader, fieldName, deserializedFish)) { + if ("id".equals(fieldName)) { + deserializedFish.id = reader.getString(); + } else if ("name".equals(fieldName)) { + deserializedFish.name = reader.getString(); + } else if ("kind".equals(fieldName)) { + deserializedFish.kind = reader.getString(); + } else if ("age".equals(fieldName)) { + deserializedFish.age = reader.getInt(); + } else if ("color".equals(fieldName)) { + deserializedFish.color = reader.getString(); + } else { reader.skipChildren(); } } @@ -271,25 +280,4 @@ static Fish fromJsonKnownDiscriminator(JsonReader jsonReader) throws IOException return deserializedFish; }); } - - @Generated - static boolean fromJsonShared(JsonReader reader, String fieldName, Fish deserializedFish) throws IOException { - if ("id".equals(fieldName)) { - deserializedFish.id = reader.getString(); - return true; - } else if ("name".equals(fieldName)) { - deserializedFish.name = reader.getString(); - return true; - } else if ("kind".equals(fieldName)) { - deserializedFish.kind = reader.getString(); - return true; - } else if ("age".equals(fieldName)) { - deserializedFish.age = reader.getInt(); - return true; - } else if ("color".equals(fieldName)) { - deserializedFish.color = reader.getString(); - return true; - } - return false; - } } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Salmon.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Salmon.java index 339026314b..475b15ac32 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Salmon.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Salmon.java @@ -22,6 +22,12 @@ */ @Fluent public final class Salmon extends Fish { + /* + * Discriminator property for Fish. + */ + @Generated + private String kind = "salmon"; + /* * The friends property. */ @@ -51,7 +57,17 @@ public final class Salmon extends Fish { */ @Generated public Salmon() { - this.kind = "salmon"; + } + + /** + * Get the kind property: Discriminator property for Fish. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -230,8 +246,16 @@ public static Salmon fromJson(JsonReader jsonReader) throws IOException { String fieldName = reader.getFieldName(); reader.nextToken(); - if (Fish.fromJsonShared(reader, fieldName, deserializedSalmon)) { - continue; + if ("id".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setId(deserializedSalmon, reader.getString()); + } else if ("name".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setName(deserializedSalmon, reader.getString()); + } else if ("age".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setAge(deserializedSalmon, reader.getInt()); + } else if ("color".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setColor(deserializedSalmon, reader.getString()); + } else if ("kind".equals(fieldName)) { + deserializedSalmon.kind = reader.getString(); } else if ("friends".equals(fieldName)) { List friends = reader.readArray(reader1 -> Fish.fromJson(reader1)); deserializedSalmon.friends = friends; diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/SawShark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/SawShark.java index 9be54bb8b6..f08773eb1b 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/SawShark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/SawShark.java @@ -19,6 +19,18 @@ */ @Fluent public final class SawShark extends Shark { + /* + * Discriminator property for Fish. + */ + @Generated + private String kind = "shark"; + + /* + * The sharktype property. + */ + @Generated + private String sharktype = "saw"; + /** * Stores updated model property, the value is property name, not serialized name. */ @@ -30,8 +42,28 @@ public final class SawShark extends Shark { */ @Generated public SawShark() { - this.kind = "shark"; - this.sharktype = "saw"; + } + + /** + * Get the kind property: Discriminator property for Fish. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; + } + + /** + * Get the sharktype property: The sharktype property. + * + * @return the sharktype value. + */ + @Generated + @Override + public String getSharktype() { + return this.sharktype; } /** @@ -128,8 +160,19 @@ public static SawShark fromJson(JsonReader jsonReader) throws IOException { String fieldName = reader.getFieldName(); reader.nextToken(); - if (Shark.fromJsonShared(reader, fieldName, deserializedSawShark)) { - continue; + if ("id".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setId(deserializedSawShark, reader.getString()); + } else if ("name".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setName(deserializedSawShark, reader.getString()); + } else if ("age".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setAge(deserializedSawShark, reader.getInt()); + } else if ("color".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setColor(deserializedSawShark, reader.getString()); + } else if ("weight".equals(fieldName)) { + JsonMergePatchHelper.getSharkAccessor() + .setWeight(deserializedSawShark, reader.getNullable(JsonReader::getInt)); + } else if ("sharktype".equals(fieldName)) { + deserializedSawShark.sharktype = reader.getString(); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Shark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Shark.java index 0e383f40ac..0e22b21bdb 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Shark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/cadl/patch/models/Shark.java @@ -19,11 +19,17 @@ */ @Fluent public class Shark extends Fish { + /* + * Discriminator property for Fish. + */ + @Generated + private String kind = "shark"; + /* * The sharktype property. */ @Generated - String sharktype; + private String sharktype = "shark"; /* * The weight property. @@ -51,8 +57,17 @@ public void setWeight(Shark model, Integer weight) { */ @Generated public Shark() { - this.kind = "shark"; - this.sharktype = "shark"; + } + + /** + * Get the kind property: Discriminator property for Fish. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -197,7 +212,19 @@ static Shark fromJsonKnownDiscriminator(JsonReader jsonReader) throws IOExceptio String fieldName = reader.getFieldName(); reader.nextToken(); - if (!Shark.fromJsonShared(reader, fieldName, deserializedShark)) { + if ("id".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setId(deserializedShark, reader.getString()); + } else if ("name".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setName(deserializedShark, reader.getString()); + } else if ("age".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setAge(deserializedShark, reader.getInt()); + } else if ("color".equals(fieldName)) { + JsonMergePatchHelper.getFishAccessor().setColor(deserializedShark, reader.getString()); + } else if ("sharktype".equals(fieldName)) { + deserializedShark.sharktype = reader.getString(); + } else if ("weight".equals(fieldName)) { + deserializedShark.weight = reader.getNullable(JsonReader::getInt); + } else { reader.skipChildren(); } } @@ -205,18 +232,4 @@ static Shark fromJsonKnownDiscriminator(JsonReader jsonReader) throws IOExceptio return deserializedShark; }); } - - @Generated - static boolean fromJsonShared(JsonReader reader, String fieldName, Shark deserializedShark) throws IOException { - if (Fish.fromJsonShared(reader, fieldName, deserializedShark)) { - return true; - } else if ("sharktype".equals(fieldName)) { - deserializedShark.sharktype = reader.getString(); - return true; - } else if ("weight".equals(fieldName)) { - deserializedShark.weight = reader.getNullable(JsonReader::getInt); - return true; - } - return false; - } } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasAsyncClient.java index cfd77d351b..6bb61d0274 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.parameters.spread.implementation.AliasImpl; -import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest1; +import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest; import com.parameters.spread.implementation.models.SpreadAsRequestParameterRequest; import com.parameters.spread.implementation.models.SpreadParameterWithInnerAliasRequest; import com.parameters.spread.implementation.models.SpreadParameterWithInnerModelRequest; @@ -205,7 +205,7 @@ public Mono> spreadParameterWithInnerAliasWithResponse(String id, public Mono spreadAsRequestBody(String name) { // Generated convenience method for spreadAsRequestBodyWithResponse RequestOptions requestOptions = new RequestOptions(); - SpreadAsRequestBodyRequest1 spreadAsRequestBodyRequestObj = new SpreadAsRequestBodyRequest1(name); + SpreadAsRequestBodyRequest spreadAsRequestBodyRequestObj = new SpreadAsRequestBodyRequest(name); BinaryData spreadAsRequestBodyRequest = BinaryData.fromObject(spreadAsRequestBodyRequestObj); return spreadAsRequestBodyWithResponse(spreadAsRequestBodyRequest, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasClient.java index 6964882f2f..fbc955011e 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/AliasClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.parameters.spread.implementation.AliasImpl; -import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest1; +import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest; import com.parameters.spread.implementation.models.SpreadAsRequestParameterRequest; import com.parameters.spread.implementation.models.SpreadParameterWithInnerAliasRequest; import com.parameters.spread.implementation.models.SpreadParameterWithInnerModelRequest; @@ -202,7 +202,7 @@ public Response spreadParameterWithInnerAliasWithResponse(String id, Strin public void spreadAsRequestBody(String name) { // Generated convenience method for spreadAsRequestBodyWithResponse RequestOptions requestOptions = new RequestOptions(); - SpreadAsRequestBodyRequest1 spreadAsRequestBodyRequestObj = new SpreadAsRequestBodyRequest1(name); + SpreadAsRequestBodyRequest spreadAsRequestBodyRequestObj = new SpreadAsRequestBodyRequest(name); BinaryData spreadAsRequestBodyRequest = BinaryData.fromObject(spreadAsRequestBodyRequestObj); spreadAsRequestBodyWithResponse(spreadAsRequestBodyRequest, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelAsyncClient.java index d1f6411eb9..d119735f9f 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.parameters.spread.implementation.ModelsImpl; -import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest; +import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest1; import com.parameters.spread.implementation.models.SpreadCompositeRequestMixRequest; import com.parameters.spread.models.BodyParameter; import reactor.core.publisher.Mono; @@ -181,7 +181,7 @@ public Mono> spreadCompositeRequestMixWithResponse(String name, S public Mono spreadAsRequestBody(String name) { // Generated convenience method for spreadAsRequestBodyWithResponse RequestOptions requestOptions = new RequestOptions(); - SpreadAsRequestBodyRequest spreadAsRequestBodyRequest1Obj = new SpreadAsRequestBodyRequest(name); + SpreadAsRequestBodyRequest1 spreadAsRequestBodyRequest1Obj = new SpreadAsRequestBodyRequest1(name); BinaryData spreadAsRequestBodyRequest1 = BinaryData.fromObject(spreadAsRequestBodyRequest1Obj); return spreadAsRequestBodyWithResponse(spreadAsRequestBodyRequest1, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelClient.java index 58eb3be14e..197a341c12 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/parameters/spread/ModelClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.parameters.spread.implementation.ModelsImpl; -import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest; +import com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest1; import com.parameters.spread.implementation.models.SpreadCompositeRequestMixRequest; import com.parameters.spread.models.BodyParameter; @@ -178,7 +178,7 @@ public Response spreadCompositeRequestMixWithResponse(String name, String public void spreadAsRequestBody(String name) { // Generated convenience method for spreadAsRequestBodyWithResponse RequestOptions requestOptions = new RequestOptions(); - SpreadAsRequestBodyRequest spreadAsRequestBodyRequest1Obj = new SpreadAsRequestBodyRequest(name); + SpreadAsRequestBodyRequest1 spreadAsRequestBodyRequest1Obj = new SpreadAsRequestBodyRequest1(name); BinaryData spreadAsRequestBodyRequest1 = BinaryData.fromObject(spreadAsRequestBodyRequest1Obj); spreadAsRequestBodyWithResponse(spreadAsRequestBodyRequest1, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Cobra.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Cobra.java index e1a2e30962..cf7169baac 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Cobra.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Cobra.java @@ -16,6 +16,12 @@ */ @Immutable public final class Cobra extends Snake { + /* + * discriminator property + */ + @Generated + private SnakeKind kind = SnakeKind.COBRA; + /** * Creates an instance of Cobra class. * @@ -24,7 +30,17 @@ public final class Cobra extends Snake { @Generated public Cobra(int length) { super(length); - this.kind = SnakeKind.COBRA; + } + + /** + * Get the kind property: discriminator property. + * + * @return the kind value. + */ + @Generated + @Override + public SnakeKind getKind() { + return this.kind; } /** @@ -34,7 +50,8 @@ public Cobra(int length) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("length", getLength()); + jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Dog.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Dog.java index ed5732e978..d4f046b81f 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Dog.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Dog.java @@ -21,7 +21,7 @@ public class Dog implements JsonSerializable { * discriminator property */ @Generated - DogKind kind; + private DogKind kind = DogKind.fromString("Dog"); /* * Weight of the dog @@ -37,7 +37,6 @@ public class Dog implements JsonSerializable { @Generated public Dog(int weight) { this.weight = weight; - this.kind = DogKind.fromString("Dog"); } /** @@ -67,13 +66,9 @@ public int getWeight() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeIntField("weight", this.weight); jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Golden.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Golden.java index 6e0b671054..fef197d7eb 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Golden.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Golden.java @@ -16,6 +16,12 @@ */ @Immutable public final class Golden extends Dog { + /* + * discriminator property + */ + @Generated + private DogKind kind = DogKind.GOLDEN; + /** * Creates an instance of Golden class. * @@ -24,7 +30,17 @@ public final class Golden extends Dog { @Generated public Golden(int weight) { super(weight); - this.kind = DogKind.GOLDEN; + } + + /** + * Get the kind property: discriminator property. + * + * @return the kind value. + */ + @Generated + @Override + public DogKind getKind() { + return this.kind; } /** @@ -34,7 +50,8 @@ public Golden(int weight) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("weight", getWeight()); + jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Snake.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Snake.java index b92d6efba5..cea781f69a 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Snake.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumdiscriminator/models/Snake.java @@ -21,7 +21,7 @@ public class Snake implements JsonSerializable { * discriminator property */ @Generated - SnakeKind kind; + private SnakeKind kind = SnakeKind.fromString("Snake"); /* * Length of the snake @@ -37,7 +37,6 @@ public class Snake implements JsonSerializable { @Generated public Snake(int length) { this.length = length; - this.kind = SnakeKind.fromString("Snake"); } /** @@ -67,13 +66,9 @@ public int getLength() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeIntField("length", this.length); jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Fish.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Fish.java index 7fe697f6c6..238873d1f7 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Fish.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Fish.java @@ -21,7 +21,7 @@ public class Fish implements JsonSerializable { * discriminator property */ @Generated - FishKind kind; + private FishKind kind = FishKind.fromString("Fish"); /* * The age property. @@ -37,7 +37,6 @@ public class Fish implements JsonSerializable { @Generated public Fish(int age) { this.age = age; - this.kind = FishKind.fromString("Fish"); } /** @@ -67,13 +66,9 @@ public int getAge() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeIntField("age", this.age); jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/GoblinShark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/GoblinShark.java index 4eacca66e1..4670c898d8 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/GoblinShark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/GoblinShark.java @@ -16,6 +16,18 @@ */ @Immutable public final class GoblinShark extends Shark { + /* + * discriminator property + */ + @Generated + private FishKind kind = FishKind.SHARK; + + /* + * The sharktype property. + */ + @Generated + private SharkKind sharktype = SharkKind.GOBLIN; + /** * Creates an instance of GoblinShark class. * @@ -24,8 +36,28 @@ public final class GoblinShark extends Shark { @Generated public GoblinShark(int age) { super(age); - this.kind = FishKind.SHARK; - this.sharktype = SharkKind.GOBLIN; + } + + /** + * Get the kind property: discriminator property. + * + * @return the kind value. + */ + @Generated + @Override + public FishKind getKind() { + return this.kind; + } + + /** + * Get the sharktype property: The sharktype property. + * + * @return the sharktype value. + */ + @Generated + @Override + public SharkKind getSharktype() { + return this.sharktype; } /** @@ -35,7 +67,9 @@ public GoblinShark(int age) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); + jsonWriter.writeIntField("age", getAge()); + jsonWriter.writeStringField("sharktype", this.sharktype == null ? null : this.sharktype.toString()); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Salmon.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Salmon.java index aacf0fa511..add46a8de1 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Salmon.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Salmon.java @@ -19,6 +19,12 @@ */ @Fluent public final class Salmon extends Fish { + /* + * discriminator property + */ + @Generated + private FishKind kind = FishKind.SALMON; + /* * The friends property. */ @@ -45,7 +51,17 @@ public final class Salmon extends Fish { @Generated public Salmon(int age) { super(age); - this.kind = FishKind.SALMON; + } + + /** + * Get the kind property: discriminator property. + * + * @return the kind value. + */ + @Generated + @Override + public FishKind getKind() { + return this.kind; } /** @@ -121,7 +137,8 @@ public Salmon setPartner(Fish partner) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("age", getAge()); + jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); jsonWriter.writeArrayField("friends", this.friends, (writer, element) -> writer.writeJson(element)); jsonWriter.writeMapField("hate", this.hate, (writer, element) -> writer.writeJson(element)); jsonWriter.writeJsonField("partner", this.partner); diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/SawShark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/SawShark.java index 7986fca5a5..3dfdf3c573 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/SawShark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/SawShark.java @@ -16,6 +16,18 @@ */ @Immutable public final class SawShark extends Shark { + /* + * discriminator property + */ + @Generated + private FishKind kind = FishKind.SHARK; + + /* + * The sharktype property. + */ + @Generated + private SharkKind sharktype = SharkKind.SAW; + /** * Creates an instance of SawShark class. * @@ -24,8 +36,28 @@ public final class SawShark extends Shark { @Generated public SawShark(int age) { super(age); - this.kind = FishKind.SHARK; - this.sharktype = SharkKind.SAW; + } + + /** + * Get the kind property: discriminator property. + * + * @return the kind value. + */ + @Generated + @Override + public FishKind getKind() { + return this.kind; + } + + /** + * Get the sharktype property: The sharktype property. + * + * @return the sharktype value. + */ + @Generated + @Override + public SharkKind getSharktype() { + return this.sharktype; } /** @@ -35,7 +67,9 @@ public SawShark(int age) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); + jsonWriter.writeIntField("age", getAge()); + jsonWriter.writeStringField("sharktype", this.sharktype == null ? null : this.sharktype.toString()); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Shark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Shark.java index 88f6d8d492..49f2a677ad 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Shark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/enumnesteddiscriminator/models/Shark.java @@ -16,11 +16,17 @@ */ @Immutable public class Shark extends Fish { + /* + * discriminator property + */ + @Generated + private FishKind kind = FishKind.SHARK; + /* * The sharktype property. */ @Generated - SharkKind sharktype; + private SharkKind sharktype = SharkKind.fromString("shark"); /** * Creates an instance of Shark class. @@ -30,8 +36,17 @@ public class Shark extends Fish { @Generated public Shark(int age) { super(age); - this.kind = FishKind.SHARK; - this.sharktype = SharkKind.fromString("shark"); + } + + /** + * Get the kind property: discriminator property. + * + * @return the kind value. + */ + @Generated + @Override + public FishKind getKind() { + return this.kind; } /** @@ -51,13 +66,10 @@ public SharkKind getSharktype() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { - super.toJsonShared(jsonWriter); + jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString()); + jsonWriter.writeIntField("age", getAge()); jsonWriter.writeStringField("sharktype", this.sharktype == null ? null : this.sharktype.toString()); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Fish.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Fish.java index a1c40de55f..982c7bbcf0 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Fish.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Fish.java @@ -21,7 +21,7 @@ public class Fish implements JsonSerializable { * Discriminator property for Fish. */ @Generated - String kind; + private String kind = "Fish"; /* * The age property. @@ -37,7 +37,6 @@ public class Fish implements JsonSerializable { @Generated public Fish(int age) { this.age = age; - this.kind = "Fish"; } /** @@ -67,13 +66,9 @@ public int getAge() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeIntField("age", this.age); jsonWriter.writeStringField("kind", this.kind); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/GoblinShark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/GoblinShark.java index d736745470..c2fc08553f 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/GoblinShark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/GoblinShark.java @@ -16,6 +16,18 @@ */ @Immutable public final class GoblinShark extends Shark { + /* + * Discriminator property for Fish. + */ + @Generated + private String kind = "shark"; + + /* + * The sharktype property. + */ + @Generated + private String sharktype = "goblin"; + /** * Creates an instance of GoblinShark class. * @@ -24,8 +36,28 @@ public final class GoblinShark extends Shark { @Generated public GoblinShark(int age) { super(age); - this.kind = "shark"; - this.sharktype = "goblin"; + } + + /** + * Get the kind property: Discriminator property for Fish. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; + } + + /** + * Get the sharktype property: The sharktype property. + * + * @return the sharktype value. + */ + @Generated + @Override + public String getSharktype() { + return this.sharktype; } /** @@ -35,7 +67,9 @@ public GoblinShark(int age) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeStringField("kind", this.kind); + jsonWriter.writeIntField("age", getAge()); + jsonWriter.writeStringField("sharktype", this.sharktype); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Salmon.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Salmon.java index 0e2eb07bc0..44b7b1494b 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Salmon.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Salmon.java @@ -19,6 +19,12 @@ */ @Fluent public final class Salmon extends Fish { + /* + * Discriminator property for Fish. + */ + @Generated + private String kind = "salmon"; + /* * The friends property. */ @@ -45,7 +51,17 @@ public final class Salmon extends Fish { @Generated public Salmon(int age) { super(age); - this.kind = "salmon"; + } + + /** + * Get the kind property: Discriminator property for Fish. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -121,7 +137,8 @@ public Salmon setPartner(Fish partner) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("age", getAge()); + jsonWriter.writeStringField("kind", this.kind); jsonWriter.writeArrayField("friends", this.friends, (writer, element) -> writer.writeJson(element)); jsonWriter.writeMapField("hate", this.hate, (writer, element) -> writer.writeJson(element)); jsonWriter.writeJsonField("partner", this.partner); diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/SawShark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/SawShark.java index 9078c1e9d0..4120bcd9ad 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/SawShark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/SawShark.java @@ -16,6 +16,18 @@ */ @Immutable public final class SawShark extends Shark { + /* + * Discriminator property for Fish. + */ + @Generated + private String kind = "shark"; + + /* + * The sharktype property. + */ + @Generated + private String sharktype = "saw"; + /** * Creates an instance of SawShark class. * @@ -24,8 +36,28 @@ public final class SawShark extends Shark { @Generated public SawShark(int age) { super(age); - this.kind = "shark"; - this.sharktype = "saw"; + } + + /** + * Get the kind property: Discriminator property for Fish. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; + } + + /** + * Get the sharktype property: The sharktype property. + * + * @return the sharktype value. + */ + @Generated + @Override + public String getSharktype() { + return this.sharktype; } /** @@ -35,7 +67,9 @@ public SawShark(int age) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeStringField("kind", this.kind); + jsonWriter.writeIntField("age", getAge()); + jsonWriter.writeStringField("sharktype", this.sharktype); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Shark.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Shark.java index fd2e4f822e..640495c57b 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Shark.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/nesteddiscriminator/models/Shark.java @@ -16,11 +16,17 @@ */ @Immutable public class Shark extends Fish { + /* + * Discriminator property for Fish. + */ + @Generated + private String kind = "shark"; + /* * The sharktype property. */ @Generated - String sharktype; + private String sharktype = "shark"; /** * Creates an instance of Shark class. @@ -30,8 +36,17 @@ public class Shark extends Fish { @Generated public Shark(int age) { super(age); - this.kind = "shark"; - this.sharktype = "shark"; + } + + /** + * Get the kind property: Discriminator property for Fish. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -51,13 +66,10 @@ public String getSharktype() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { - super.toJsonShared(jsonWriter); + jsonWriter.writeStringField("kind", this.kind); + jsonWriter.writeIntField("age", getAge()); jsonWriter.writeStringField("sharktype", this.sharktype); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Bird.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Bird.java index 29caef9d20..6855214445 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Bird.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Bird.java @@ -21,7 +21,7 @@ public class Bird implements JsonSerializable { * The kind property. */ @Generated - String kind; + private String kind = "Bird"; /* * The wingspan property. @@ -37,7 +37,6 @@ public class Bird implements JsonSerializable { @Generated public Bird(int wingspan) { this.wingspan = wingspan; - this.kind = "Bird"; } /** @@ -67,13 +66,9 @@ public int getWingspan() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeIntField("wingspan", this.wingspan); jsonWriter.writeStringField("kind", this.kind); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Dinosaur.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Dinosaur.java index 7067fd9f90..940c920603 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Dinosaur.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Dinosaur.java @@ -21,7 +21,7 @@ public class Dinosaur implements JsonSerializable { * Discriminator property for Dinosaur. */ @Generated - String kind; + private String kind = "Dinosaur"; /* * The size property. @@ -37,7 +37,6 @@ public class Dinosaur implements JsonSerializable { @Generated protected Dinosaur(int size) { this.size = size; - this.kind = "Dinosaur"; } /** @@ -67,13 +66,9 @@ public int getSize() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeIntField("size", this.size); jsonWriter.writeStringField("kind", this.kind); + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Eagle.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Eagle.java index 6b68a67b22..c49d2e0ed3 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Eagle.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Eagle.java @@ -19,6 +19,12 @@ */ @Fluent public final class Eagle extends Bird { + /* + * The kind property. + */ + @Generated + private String kind = "eagle"; + /* * The friends property. */ @@ -45,7 +51,17 @@ public final class Eagle extends Bird { @Generated public Eagle(int wingspan) { super(wingspan); - this.kind = "eagle"; + } + + /** + * Get the kind property: The kind property. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -121,7 +137,8 @@ public Eagle setPartner(Bird partner) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("wingspan", getWingspan()); + jsonWriter.writeStringField("kind", this.kind); jsonWriter.writeArrayField("friends", this.friends, (writer, element) -> writer.writeJson(element)); jsonWriter.writeMapField("hate", this.hate, (writer, element) -> writer.writeJson(element)); jsonWriter.writeJsonField("partner", this.partner); diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Goose.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Goose.java index 0ac82567b8..d097786368 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Goose.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Goose.java @@ -16,6 +16,12 @@ */ @Immutable public final class Goose extends Bird { + /* + * The kind property. + */ + @Generated + private String kind = "goose"; + /** * Creates an instance of Goose class. * @@ -24,7 +30,17 @@ public final class Goose extends Bird { @Generated public Goose(int wingspan) { super(wingspan); - this.kind = "goose"; + } + + /** + * Get the kind property: The kind property. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -34,7 +50,8 @@ public Goose(int wingspan) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("wingspan", getWingspan()); + jsonWriter.writeStringField("kind", this.kind); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/SeaGull.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/SeaGull.java index 8f7f1db554..07256e55c6 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/SeaGull.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/SeaGull.java @@ -16,6 +16,12 @@ */ @Immutable public final class SeaGull extends Bird { + /* + * The kind property. + */ + @Generated + private String kind = "seagull"; + /** * Creates an instance of SeaGull class. * @@ -24,7 +30,17 @@ public final class SeaGull extends Bird { @Generated public SeaGull(int wingspan) { super(wingspan); - this.kind = "seagull"; + } + + /** + * Get the kind property: The kind property. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -34,7 +50,8 @@ public SeaGull(int wingspan) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("wingspan", getWingspan()); + jsonWriter.writeStringField("kind", this.kind); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Sparrow.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Sparrow.java index b9a00e0c16..93da94c4b7 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Sparrow.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/Sparrow.java @@ -16,6 +16,12 @@ */ @Immutable public final class Sparrow extends Bird { + /* + * The kind property. + */ + @Generated + private String kind = "sparrow"; + /** * Creates an instance of Sparrow class. * @@ -24,7 +30,17 @@ public final class Sparrow extends Bird { @Generated public Sparrow(int wingspan) { super(wingspan); - this.kind = "sparrow"; + } + + /** + * Get the kind property: The kind property. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -34,7 +50,8 @@ public Sparrow(int wingspan) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("wingspan", getWingspan()); + jsonWriter.writeStringField("kind", this.kind); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/TRex.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/TRex.java index d0ccee9b82..90902817d5 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/TRex.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/model/inheritance/singlediscriminator/models/TRex.java @@ -16,6 +16,12 @@ */ @Immutable public final class TRex extends Dinosaur { + /* + * Discriminator property for Dinosaur. + */ + @Generated + private String kind = "t-rex"; + /** * Creates an instance of TRex class. * @@ -24,7 +30,17 @@ public final class TRex extends Dinosaur { @Generated private TRex(int size) { super(size); - this.kind = "t-rex"; + } + + /** + * Get the kind property: Discriminator property for Dinosaur. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -34,7 +50,8 @@ private TRex(int size) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeIntField("size", getSize()); + jsonWriter.writeStringField("kind", this.kind); return jsonWriter.writeEndObject(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminated.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminated.java index 59b128de47..c336bbe475 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminated.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminated.java @@ -24,7 +24,7 @@ public class ExtendsUnknownAdditionalPropertiesDiscriminated * The discriminator */ @Generated - String kind; + private String kind = "ExtendsUnknownAdditionalPropertiesDiscriminated"; /* * The name property @@ -46,7 +46,6 @@ public class ExtendsUnknownAdditionalPropertiesDiscriminated @Generated public ExtendsUnknownAdditionalPropertiesDiscriminated(String name) { this.name = name; - this.kind = "ExtendsUnknownAdditionalPropertiesDiscriminated"; } /** @@ -99,11 +98,6 @@ public Map getAdditionalProperties() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStringField("name", this.name); jsonWriter.writeStringField("kind", this.kind); if (additionalProperties != null) { @@ -111,6 +105,7 @@ void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeUntypedField(additionalProperty.getKey(), additionalProperty.getValue()); } } + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminatedDerived.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminatedDerived.java index 029927ebd5..7fb248f4d3 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminatedDerived.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/ExtendsUnknownAdditionalPropertiesDiscriminatedDerived.java @@ -19,6 +19,12 @@ @Fluent public final class ExtendsUnknownAdditionalPropertiesDiscriminatedDerived extends ExtendsUnknownAdditionalPropertiesDiscriminated { + /* + * The discriminator + */ + @Generated + private String kind = "derived"; + /* * The index property */ @@ -41,7 +47,17 @@ public final class ExtendsUnknownAdditionalPropertiesDiscriminatedDerived public ExtendsUnknownAdditionalPropertiesDiscriminatedDerived(String name, int index) { super(name); this.index = index; - this.kind = "derived"; + } + + /** + * Get the kind property: The discriminator. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -83,8 +99,9 @@ public ExtendsUnknownAdditionalPropertiesDiscriminatedDerived setAge(Double age) @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeStringField("name", getName()); jsonWriter.writeIntField("index", this.index); + jsonWriter.writeStringField("kind", this.kind); jsonWriter.writeNumberField("age", this.age); if (getAdditionalProperties() != null) { for (Map.Entry additionalProperty : getAdditionalProperties().entrySet()) { diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminated.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminated.java index b0192eb43f..91dcdb0987 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminated.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminated.java @@ -24,7 +24,7 @@ public class IsUnknownAdditionalPropertiesDiscriminated * The discriminator */ @Generated - String kind; + private String kind = "IsUnknownAdditionalPropertiesDiscriminated"; /* * The name property @@ -46,7 +46,6 @@ public class IsUnknownAdditionalPropertiesDiscriminated @Generated public IsUnknownAdditionalPropertiesDiscriminated(String name) { this.name = name; - this.kind = "IsUnknownAdditionalPropertiesDiscriminated"; } /** @@ -99,11 +98,6 @@ public Map getAdditionalProperties() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); - return jsonWriter.writeEndObject(); - } - - void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStringField("name", this.name); jsonWriter.writeStringField("kind", this.kind); if (additionalProperties != null) { @@ -111,6 +105,7 @@ void toJsonShared(JsonWriter jsonWriter) throws IOException { jsonWriter.writeUntypedField(additionalProperty.getKey(), additionalProperty.getValue()); } } + return jsonWriter.writeEndObject(); } /** diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminatedDerived.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminatedDerived.java index 692ee746d5..3d3c6049c1 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminatedDerived.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/property/additionalproperties/models/IsUnknownAdditionalPropertiesDiscriminatedDerived.java @@ -19,6 +19,12 @@ @Fluent public final class IsUnknownAdditionalPropertiesDiscriminatedDerived extends IsUnknownAdditionalPropertiesDiscriminated { + /* + * The discriminator + */ + @Generated + private String kind = "derived"; + /* * The index property */ @@ -41,7 +47,17 @@ public final class IsUnknownAdditionalPropertiesDiscriminatedDerived public IsUnknownAdditionalPropertiesDiscriminatedDerived(String name, int index) { super(name); this.index = index; - this.kind = "derived"; + } + + /** + * Get the kind property: The discriminator. + * + * @return the kind value. + */ + @Generated + @Override + public String getKind() { + return this.kind; } /** @@ -83,8 +99,9 @@ public IsUnknownAdditionalPropertiesDiscriminatedDerived setAge(Double age) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - toJsonShared(jsonWriter); + jsonWriter.writeStringField("name", getName()); jsonWriter.writeIntField("index", this.index); + jsonWriter.writeStringField("kind", this.kind); jsonWriter.writeNumberField("age", this.age); if (getAdditionalProperties() != null) { for (Map.Entry additionalProperty : getAdditionalProperties().entrySet()) { diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyAsyncClient.java index 2ffdd54d23..a3a0c16ae9 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.EnumsOnliesImpl; -import com.type.union.implementation.models.SendRequest6; +import com.type.union.implementation.models.SendRequest3; import com.type.union.models.EnumsOnlyCases; import com.type.union.models.GetResponse3; import reactor.core.publisher.Mono; @@ -129,7 +129,7 @@ public Mono get() { public Mono send(EnumsOnlyCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest6 sendRequest3Obj = new SendRequest6(prop); + SendRequest3 sendRequest3Obj = new SendRequest3(prop); BinaryData sendRequest3 = BinaryData.fromObject(sendRequest3Obj); return sendWithResponse(sendRequest3, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyClient.java index 769c874663..9c98e8630b 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/EnumsOnlyClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.EnumsOnliesImpl; -import com.type.union.implementation.models.SendRequest6; +import com.type.union.implementation.models.SendRequest3; import com.type.union.models.EnumsOnlyCases; import com.type.union.models.GetResponse3; @@ -125,7 +125,7 @@ public GetResponse3 get() { public void send(EnumsOnlyCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest6 sendRequest3Obj = new SendRequest6(prop); + SendRequest3 sendRequest3Obj = new SendRequest3(prop); BinaryData sendRequest3 = BinaryData.fromObject(sendRequest3Obj); sendWithResponse(sendRequest3, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyAsyncClient.java index 36aded8ce8..c7dfaa4e0c 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.FloatsOnliesImpl; -import com.type.union.implementation.models.SendRequest4; +import com.type.union.implementation.models.SendRequest5; import com.type.union.models.GetResponse5; import com.type.union.models.GetResponseProp1; import reactor.core.publisher.Mono; @@ -123,7 +123,7 @@ public Mono get() { public Mono send(GetResponseProp1 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest4 sendRequest5Obj = new SendRequest4(prop); + SendRequest5 sendRequest5Obj = new SendRequest5(prop); BinaryData sendRequest5 = BinaryData.fromObject(sendRequest5Obj); return sendWithResponse(sendRequest5, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyClient.java index 89810763ef..8c55f70516 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/FloatsOnlyClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.FloatsOnliesImpl; -import com.type.union.implementation.models.SendRequest4; +import com.type.union.implementation.models.SendRequest5; import com.type.union.models.GetResponse5; import com.type.union.models.GetResponseProp1; @@ -119,7 +119,7 @@ public GetResponse5 get() { public void send(GetResponseProp1 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest4 sendRequest5Obj = new SendRequest4(prop); + SendRequest5 sendRequest5Obj = new SendRequest5(prop); BinaryData sendRequest5 = BinaryData.fromObject(sendRequest5Obj); sendWithResponse(sendRequest5, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyAsyncClient.java index 37dae5d8a3..05cebee5eb 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.IntsOnliesImpl; -import com.type.union.implementation.models.SendRequest3; +import com.type.union.implementation.models.SendRequest6; import com.type.union.models.GetResponse6; import com.type.union.models.GetResponseProp2; import reactor.core.publisher.Mono; @@ -123,7 +123,7 @@ public Mono get() { public Mono send(GetResponseProp2 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest3 sendRequest6Obj = new SendRequest3(prop); + SendRequest6 sendRequest6Obj = new SendRequest6(prop); BinaryData sendRequest6 = BinaryData.fromObject(sendRequest6Obj); return sendWithResponse(sendRequest6, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyClient.java index 41afa4d634..d902d9d83c 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/IntsOnlyClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.IntsOnliesImpl; -import com.type.union.implementation.models.SendRequest3; +import com.type.union.implementation.models.SendRequest6; import com.type.union.models.GetResponse6; import com.type.union.models.GetResponseProp2; @@ -119,7 +119,7 @@ public GetResponse6 get() { public void send(GetResponseProp2 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest3 sendRequest6Obj = new SendRequest3(prop); + SendRequest6 sendRequest6Obj = new SendRequest6(prop); BinaryData sendRequest6 = BinaryData.fromObject(sendRequest6Obj); sendWithResponse(sendRequest6, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsAsyncClient.java index 7f0d86c52e..a4295e1bc7 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.MixedLiteralsImpl; -import com.type.union.implementation.models.SendRequest8; +import com.type.union.implementation.models.SendRequest1; import com.type.union.models.GetResponse1; import com.type.union.models.MixedLiteralsCases; import reactor.core.publisher.Mono; @@ -133,7 +133,7 @@ public Mono get() { public Mono send(MixedLiteralsCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest8 sendRequest1Obj = new SendRequest8(prop); + SendRequest1 sendRequest1Obj = new SendRequest1(prop); BinaryData sendRequest1 = BinaryData.fromObject(sendRequest1Obj); return sendWithResponse(sendRequest1, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsClient.java index afbedea7b9..da5a728cac 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedLiteralsClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.MixedLiteralsImpl; -import com.type.union.implementation.models.SendRequest8; +import com.type.union.implementation.models.SendRequest1; import com.type.union.models.GetResponse1; import com.type.union.models.MixedLiteralsCases; @@ -129,7 +129,7 @@ public GetResponse1 get() { public void send(MixedLiteralsCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest8 sendRequest1Obj = new SendRequest8(prop); + SendRequest1 sendRequest1Obj = new SendRequest1(prop); BinaryData sendRequest1 = BinaryData.fromObject(sendRequest1Obj); sendWithResponse(sendRequest1, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesAsyncClient.java index b656c96c63..9c68d5f92d 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.MixedTypesImpl; -import com.type.union.implementation.models.SendRequest9; +import com.type.union.implementation.models.SendRequest; import com.type.union.models.GetResponse; import com.type.union.models.MixedTypesCases; import reactor.core.publisher.Mono; @@ -139,7 +139,7 @@ public Mono get() { public Mono send(MixedTypesCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest9 sendRequestObj = new SendRequest9(prop); + SendRequest sendRequestObj = new SendRequest(prop); BinaryData sendRequest = BinaryData.fromObject(sendRequestObj); return sendWithResponse(sendRequest, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesClient.java index a289ac1d2d..84e732c16f 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/MixedTypesClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.MixedTypesImpl; -import com.type.union.implementation.models.SendRequest9; +import com.type.union.implementation.models.SendRequest; import com.type.union.models.GetResponse; import com.type.union.models.MixedTypesCases; @@ -135,7 +135,7 @@ public GetResponse get() { public void send(MixedTypesCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest9 sendRequestObj = new SendRequest9(prop); + SendRequest sendRequestObj = new SendRequest(prop); BinaryData sendRequest = BinaryData.fromObject(sendRequestObj); sendWithResponse(sendRequest, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyAsyncClient.java index 38bd977e66..c7cdd0ba3a 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.ModelsOnliesImpl; -import com.type.union.implementation.models.SendRequest5; +import com.type.union.implementation.models.SendRequest4; import com.type.union.models.GetResponse4; import reactor.core.publisher.Mono; @@ -122,7 +122,7 @@ public Mono get() { public Mono send(BinaryData prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest5 sendRequest4Obj = new SendRequest5(prop); + SendRequest4 sendRequest4Obj = new SendRequest4(prop); BinaryData sendRequest4 = BinaryData.fromObject(sendRequest4Obj); return sendWithResponse(sendRequest4, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyClient.java index 127f6c388e..449f17d309 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/ModelsOnlyClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.ModelsOnliesImpl; -import com.type.union.implementation.models.SendRequest5; +import com.type.union.implementation.models.SendRequest4; import com.type.union.models.GetResponse4; /** @@ -118,7 +118,7 @@ public GetResponse4 get() { public void send(BinaryData prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest5 sendRequest4Obj = new SendRequest5(prop); + SendRequest4 sendRequest4Obj = new SendRequest4(prop); BinaryData sendRequest4 = BinaryData.fromObject(sendRequest4Obj); sendWithResponse(sendRequest4, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayAsyncClient.java index a014aafd67..4be4148f82 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.StringAndArraysImpl; -import com.type.union.implementation.models.SendRequest7; +import com.type.union.implementation.models.SendRequest2; import com.type.union.models.GetResponse2; import com.type.union.models.StringAndArrayCases; import reactor.core.publisher.Mono; @@ -129,7 +129,7 @@ public Mono get() { public Mono send(StringAndArrayCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest7 sendRequest2Obj = new SendRequest7(prop); + SendRequest2 sendRequest2Obj = new SendRequest2(prop); BinaryData sendRequest2 = BinaryData.fromObject(sendRequest2Obj); return sendWithResponse(sendRequest2, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayClient.java index 73dce75b70..317d515c65 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringAndArrayClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.StringAndArraysImpl; -import com.type.union.implementation.models.SendRequest7; +import com.type.union.implementation.models.SendRequest2; import com.type.union.models.GetResponse2; import com.type.union.models.StringAndArrayCases; @@ -125,7 +125,7 @@ public GetResponse2 get() { public void send(StringAndArrayCases prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest7 sendRequest2Obj = new SendRequest7(prop); + SendRequest2 sendRequest2Obj = new SendRequest2(prop); BinaryData sendRequest2 = BinaryData.fromObject(sendRequest2Obj); sendWithResponse(sendRequest2, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleAsyncClient.java index ca9899924e..da05e0f26f 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.StringExtensiblesImpl; -import com.type.union.implementation.models.SendRequest1; +import com.type.union.implementation.models.SendRequest8; import com.type.union.models.GetResponse8; import com.type.union.models.GetResponseProp3; import reactor.core.publisher.Mono; @@ -123,7 +123,7 @@ public Mono get() { public Mono send(GetResponseProp3 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest1 sendRequest8Obj = new SendRequest1(prop); + SendRequest8 sendRequest8Obj = new SendRequest8(prop); BinaryData sendRequest8 = BinaryData.fromObject(sendRequest8Obj); return sendWithResponse(sendRequest8, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleClient.java index c6f749c45f..4364649650 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.StringExtensiblesImpl; -import com.type.union.implementation.models.SendRequest1; +import com.type.union.implementation.models.SendRequest8; import com.type.union.models.GetResponse8; import com.type.union.models.GetResponseProp3; @@ -119,7 +119,7 @@ public GetResponse8 get() { public void send(GetResponseProp3 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest1 sendRequest8Obj = new SendRequest1(prop); + SendRequest8 sendRequest8Obj = new SendRequest8(prop); BinaryData sendRequest8 = BinaryData.fromObject(sendRequest8Obj); sendWithResponse(sendRequest8, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedAsyncClient.java index 99f53508a1..70756efb11 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.StringExtensibleNamedsImpl; -import com.type.union.implementation.models.SendRequest2; +import com.type.union.implementation.models.SendRequest7; import com.type.union.models.GetResponse7; import com.type.union.models.StringExtensibleNamedUnion; import reactor.core.publisher.Mono; @@ -123,7 +123,7 @@ public Mono get() { public Mono send(StringExtensibleNamedUnion prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest2 sendRequest7Obj = new SendRequest2(prop); + SendRequest7 sendRequest7Obj = new SendRequest7(prop); BinaryData sendRequest7 = BinaryData.fromObject(sendRequest7Obj); return sendWithResponse(sendRequest7, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedClient.java index 1f46812f08..0052206aee 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringExtensibleNamedClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.StringExtensibleNamedsImpl; -import com.type.union.implementation.models.SendRequest2; +import com.type.union.implementation.models.SendRequest7; import com.type.union.models.GetResponse7; import com.type.union.models.StringExtensibleNamedUnion; @@ -119,7 +119,7 @@ public GetResponse7 get() { public void send(StringExtensibleNamedUnion prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest2 sendRequest7Obj = new SendRequest2(prop); + SendRequest7 sendRequest7Obj = new SendRequest7(prop); BinaryData sendRequest7 = BinaryData.fromObject(sendRequest7Obj); sendWithResponse(sendRequest7, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyAsyncClient.java index 33f7f84828..a94ecce3f3 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyAsyncClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyAsyncClient.java @@ -17,7 +17,7 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; import com.type.union.implementation.StringsOnliesImpl; -import com.type.union.implementation.models.SendRequest; +import com.type.union.implementation.models.SendRequest9; import com.type.union.models.GetResponse9; import com.type.union.models.GetResponseProp4; import reactor.core.publisher.Mono; @@ -123,7 +123,7 @@ public Mono get() { public Mono send(GetResponseProp4 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest sendRequest9Obj = new SendRequest(prop); + SendRequest9 sendRequest9Obj = new SendRequest9(prop); BinaryData sendRequest9 = BinaryData.fromObject(sendRequest9Obj); return sendWithResponse(sendRequest9, requestOptions).flatMap(FluxUtil::toMono); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyClient.java index 2700b5b7ad..8e96ecdc6d 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyClient.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/StringsOnlyClient.java @@ -16,7 +16,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.BinaryData; import com.type.union.implementation.StringsOnliesImpl; -import com.type.union.implementation.models.SendRequest; +import com.type.union.implementation.models.SendRequest9; import com.type.union.models.GetResponse9; import com.type.union.models.GetResponseProp4; @@ -119,7 +119,7 @@ public GetResponse9 get() { public void send(GetResponseProp4 prop) { // Generated convenience method for sendWithResponse RequestOptions requestOptions = new RequestOptions(); - SendRequest sendRequest9Obj = new SendRequest(prop); + SendRequest9 sendRequest9Obj = new SendRequest9(prop); BinaryData sendRequest9 = BinaryData.fromObject(sendRequest9Obj); sendWithResponse(sendRequest9, requestOptions).getValue(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest.java index 61d84b4934..3d0d1a2dcf 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.GetResponseProp4; +import com.type.union.models.MixedTypesCases; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest implements JsonSerializable { * The prop property. */ @Generated - private final GetResponseProp4 prop; + private final MixedTypesCases prop; /** * Creates an instance of SendRequest class. @@ -30,7 +30,7 @@ public final class SendRequest implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest(GetResponseProp4 prop) { + public SendRequest(MixedTypesCases prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest(GetResponseProp4 prop) { * @return the prop value. */ @Generated - public GetResponseProp4 getProp() { + public MixedTypesCases getProp() { return this.prop; } @@ -51,7 +51,7 @@ public GetResponseProp4 getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeStringField("prop", this.prop == null ? null : this.prop.toString()); + jsonWriter.writeJsonField("prop", this.prop); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - GetResponseProp4 prop = null; + MixedTypesCases prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = GetResponseProp4.fromString(reader.getString()); + prop = MixedTypesCases.fromJson(reader); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest1.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest1.java index 6d355540c9..555d9471ae 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest1.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest1.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.GetResponseProp3; +import com.type.union.models.MixedLiteralsCases; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest1 implements JsonSerializable { * The prop property. */ @Generated - private final GetResponseProp3 prop; + private final MixedLiteralsCases prop; /** * Creates an instance of SendRequest1 class. @@ -30,7 +30,7 @@ public final class SendRequest1 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest1(GetResponseProp3 prop) { + public SendRequest1(MixedLiteralsCases prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest1(GetResponseProp3 prop) { * @return the prop value. */ @Generated - public GetResponseProp3 getProp() { + public MixedLiteralsCases getProp() { return this.prop; } @@ -51,7 +51,7 @@ public GetResponseProp3 getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeStringField("prop", this.prop == null ? null : this.prop.toString()); + jsonWriter.writeJsonField("prop", this.prop); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest1 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - GetResponseProp3 prop = null; + MixedLiteralsCases prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = GetResponseProp3.fromString(reader.getString()); + prop = MixedLiteralsCases.fromJson(reader); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest2.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest2.java index ed771bc80f..f76d27c2b3 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest2.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest2.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.StringExtensibleNamedUnion; +import com.type.union.models.StringAndArrayCases; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest2 implements JsonSerializable { * The prop property. */ @Generated - private final StringExtensibleNamedUnion prop; + private final StringAndArrayCases prop; /** * Creates an instance of SendRequest2 class. @@ -30,7 +30,7 @@ public final class SendRequest2 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest2(StringExtensibleNamedUnion prop) { + public SendRequest2(StringAndArrayCases prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest2(StringExtensibleNamedUnion prop) { * @return the prop value. */ @Generated - public StringExtensibleNamedUnion getProp() { + public StringAndArrayCases getProp() { return this.prop; } @@ -51,7 +51,7 @@ public StringExtensibleNamedUnion getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeStringField("prop", this.prop == null ? null : this.prop.toString()); + jsonWriter.writeJsonField("prop", this.prop); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest2 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - StringExtensibleNamedUnion prop = null; + StringAndArrayCases prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = StringExtensibleNamedUnion.fromString(reader.getString()); + prop = StringAndArrayCases.fromJson(reader); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest3.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest3.java index d3970dfa97..2167fccfa4 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest3.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest3.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.GetResponseProp2; +import com.type.union.models.EnumsOnlyCases; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest3 implements JsonSerializable { * The prop property. */ @Generated - private final GetResponseProp2 prop; + private final EnumsOnlyCases prop; /** * Creates an instance of SendRequest3 class. @@ -30,7 +30,7 @@ public final class SendRequest3 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest3(GetResponseProp2 prop) { + public SendRequest3(EnumsOnlyCases prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest3(GetResponseProp2 prop) { * @return the prop value. */ @Generated - public GetResponseProp2 getProp() { + public EnumsOnlyCases getProp() { return this.prop; } @@ -51,7 +51,7 @@ public GetResponseProp2 getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeNumberField("prop", this.prop == null ? null : this.prop.toInt()); + jsonWriter.writeJsonField("prop", this.prop); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest3 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - GetResponseProp2 prop = null; + EnumsOnlyCases prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = GetResponseProp2.fromInt(reader.getInt()); + prop = EnumsOnlyCases.fromJson(reader); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest4.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest4.java index 923bcefe93..023b10dad0 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest4.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest4.java @@ -6,11 +6,11 @@ import com.azure.core.annotation.Generated; import com.azure.core.annotation.Immutable; +import com.azure.core.util.BinaryData; import com.azure.json.JsonReader; import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.GetResponseProp1; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest4 implements JsonSerializable { * The prop property. */ @Generated - private final GetResponseProp1 prop; + private final BinaryData prop; /** * Creates an instance of SendRequest4 class. @@ -30,7 +30,7 @@ public final class SendRequest4 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest4(GetResponseProp1 prop) { + public SendRequest4(BinaryData prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest4(GetResponseProp1 prop) { * @return the prop value. */ @Generated - public GetResponseProp1 getProp() { + public BinaryData getProp() { return this.prop; } @@ -51,7 +51,7 @@ public GetResponseProp1 getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeNumberField("prop", this.prop == null ? null : this.prop.toDouble()); + jsonWriter.writeUntypedField("prop", this.prop.toObject(Object.class)); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest4 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - GetResponseProp1 prop = null; + BinaryData prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = GetResponseProp1.fromDouble(reader.getDouble()); + prop = reader.getNullable(nonNullReader -> BinaryData.fromObject(nonNullReader.readUntyped())); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest5.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest5.java index 02a3d5d946..cd25adc21e 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest5.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest5.java @@ -6,11 +6,11 @@ import com.azure.core.annotation.Generated; import com.azure.core.annotation.Immutable; -import com.azure.core.util.BinaryData; import com.azure.json.JsonReader; import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; +import com.type.union.models.GetResponseProp1; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest5 implements JsonSerializable { * The prop property. */ @Generated - private final BinaryData prop; + private final GetResponseProp1 prop; /** * Creates an instance of SendRequest5 class. @@ -30,7 +30,7 @@ public final class SendRequest5 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest5(BinaryData prop) { + public SendRequest5(GetResponseProp1 prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest5(BinaryData prop) { * @return the prop value. */ @Generated - public BinaryData getProp() { + public GetResponseProp1 getProp() { return this.prop; } @@ -51,7 +51,7 @@ public BinaryData getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeUntypedField("prop", this.prop.toObject(Object.class)); + jsonWriter.writeNumberField("prop", this.prop == null ? null : this.prop.toDouble()); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest5 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - BinaryData prop = null; + GetResponseProp1 prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = reader.getNullable(nonNullReader -> BinaryData.fromObject(nonNullReader.readUntyped())); + prop = GetResponseProp1.fromDouble(reader.getDouble()); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest6.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest6.java index e6a10e8937..78602f59d9 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest6.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest6.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.EnumsOnlyCases; +import com.type.union.models.GetResponseProp2; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest6 implements JsonSerializable { * The prop property. */ @Generated - private final EnumsOnlyCases prop; + private final GetResponseProp2 prop; /** * Creates an instance of SendRequest6 class. @@ -30,7 +30,7 @@ public final class SendRequest6 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest6(EnumsOnlyCases prop) { + public SendRequest6(GetResponseProp2 prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest6(EnumsOnlyCases prop) { * @return the prop value. */ @Generated - public EnumsOnlyCases getProp() { + public GetResponseProp2 getProp() { return this.prop; } @@ -51,7 +51,7 @@ public EnumsOnlyCases getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeJsonField("prop", this.prop); + jsonWriter.writeNumberField("prop", this.prop == null ? null : this.prop.toInt()); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest6 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - EnumsOnlyCases prop = null; + GetResponseProp2 prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = EnumsOnlyCases.fromJson(reader); + prop = GetResponseProp2.fromInt(reader.getInt()); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest7.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest7.java index 686b6dd781..a01e4444f6 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest7.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest7.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.StringAndArrayCases; +import com.type.union.models.StringExtensibleNamedUnion; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest7 implements JsonSerializable { * The prop property. */ @Generated - private final StringAndArrayCases prop; + private final StringExtensibleNamedUnion prop; /** * Creates an instance of SendRequest7 class. @@ -30,7 +30,7 @@ public final class SendRequest7 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest7(StringAndArrayCases prop) { + public SendRequest7(StringExtensibleNamedUnion prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest7(StringAndArrayCases prop) { * @return the prop value. */ @Generated - public StringAndArrayCases getProp() { + public StringExtensibleNamedUnion getProp() { return this.prop; } @@ -51,7 +51,7 @@ public StringAndArrayCases getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeJsonField("prop", this.prop); + jsonWriter.writeStringField("prop", this.prop == null ? null : this.prop.toString()); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest7 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - StringAndArrayCases prop = null; + StringExtensibleNamedUnion prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = StringAndArrayCases.fromJson(reader); + prop = StringExtensibleNamedUnion.fromString(reader.getString()); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest8.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest8.java index 8681874949..bb7f2a1bae 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest8.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest8.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.MixedLiteralsCases; +import com.type.union.models.GetResponseProp3; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest8 implements JsonSerializable { * The prop property. */ @Generated - private final MixedLiteralsCases prop; + private final GetResponseProp3 prop; /** * Creates an instance of SendRequest8 class. @@ -30,7 +30,7 @@ public final class SendRequest8 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest8(MixedLiteralsCases prop) { + public SendRequest8(GetResponseProp3 prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest8(MixedLiteralsCases prop) { * @return the prop value. */ @Generated - public MixedLiteralsCases getProp() { + public GetResponseProp3 getProp() { return this.prop; } @@ -51,7 +51,7 @@ public MixedLiteralsCases getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeJsonField("prop", this.prop); + jsonWriter.writeStringField("prop", this.prop == null ? null : this.prop.toString()); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest8 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - MixedLiteralsCases prop = null; + GetResponseProp3 prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = MixedLiteralsCases.fromJson(reader); + prop = GetResponseProp3.fromString(reader.getString()); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest9.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest9.java index ba9d1e14f2..77350fb49f 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest9.java +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/com/type/union/implementation/models/SendRequest9.java @@ -10,7 +10,7 @@ import com.azure.json.JsonSerializable; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; -import com.type.union.models.MixedTypesCases; +import com.type.union.models.GetResponseProp4; import java.io.IOException; /** @@ -22,7 +22,7 @@ public final class SendRequest9 implements JsonSerializable { * The prop property. */ @Generated - private final MixedTypesCases prop; + private final GetResponseProp4 prop; /** * Creates an instance of SendRequest9 class. @@ -30,7 +30,7 @@ public final class SendRequest9 implements JsonSerializable { * @param prop the prop value to set. */ @Generated - public SendRequest9(MixedTypesCases prop) { + public SendRequest9(GetResponseProp4 prop) { this.prop = prop; } @@ -40,7 +40,7 @@ public SendRequest9(MixedTypesCases prop) { * @return the prop value. */ @Generated - public MixedTypesCases getProp() { + public GetResponseProp4 getProp() { return this.prop; } @@ -51,7 +51,7 @@ public MixedTypesCases getProp() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeJsonField("prop", this.prop); + jsonWriter.writeStringField("prop", this.prop == null ? null : this.prop.toString()); return jsonWriter.writeEndObject(); } @@ -67,13 +67,13 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Generated public static SendRequest9 fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { - MixedTypesCases prop = null; + GetResponseProp4 prop = null; while (reader.nextToken() != JsonToken.END_OBJECT) { String fieldName = reader.getFieldName(); reader.nextToken(); if ("prop".equals(fieldName)) { - prop = MixedTypesCases.fromJson(reader); + prop = GetResponseProp4.fromString(reader.getString()); } else { reader.skipChildren(); } diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/parameters-spread_apiview_properties.json b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/parameters-spread_apiview_properties.json index 80c6eab502..febc65ea91 100644 --- a/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/parameters-spread_apiview_properties.json +++ b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/parameters-spread_apiview_properties.json @@ -46,8 +46,8 @@ "com.parameters.spread.ModelClient.spreadCompositeRequestWithoutBody": "Parameters.Spread.Model.spreadCompositeRequestWithoutBody", "com.parameters.spread.ModelClient.spreadCompositeRequestWithoutBodyWithResponse": "Parameters.Spread.Model.spreadCompositeRequestWithoutBody", "com.parameters.spread.SpreadClientBuilder": "Parameters.Spread", - "com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest": "Parameters.Spread.Model.spreadAsRequestBody.Request.anonymous", - "com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest1": "Parameters.Spread.Alias.spreadAsRequestBody.Request.anonymous", + "com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest": "Parameters.Spread.Alias.spreadAsRequestBody.Request.anonymous", + "com.parameters.spread.implementation.models.SpreadAsRequestBodyRequest1": "Parameters.Spread.Model.spreadAsRequestBody.Request.anonymous", "com.parameters.spread.implementation.models.SpreadAsRequestParameterRequest": "spreadAsRequestParameter.Request.anonymous", "com.parameters.spread.implementation.models.SpreadCompositeRequestMixRequest": "spreadCompositeRequestMix.Request.anonymous", "com.parameters.spread.implementation.models.SpreadParameterWithInnerAliasRequest": "spreadParameterWithInnerAlias.Request.anonymous", diff --git a/packages/http-client-java/generator/http-client-generator/pom.xml b/packages/http-client-java/generator/http-client-generator/pom.xml index 62573f2386..971ee12c0d 100644 --- a/packages/http-client-java/generator/http-client-generator/pom.xml +++ b/packages/http-client-java/generator/http-client-generator/pom.xml @@ -12,8 +12,12 @@ 1.0.0-beta.1 - + + emitter + com.microsoft.typespec.http.client.generator.Main + + com.microsoft.typespec http-client-generator-mgmt @@ -45,33 +49,50 @@ - emitter org.apache.maven.plugins - maven-assembly-plugin + maven-shade-plugin + 3.6.0 package - single + shade - false - - - com.microsoft.typespec.http.client.generator.Main - - - - jar-with-dependencies - + ${shade.finalName} + true + + ${shade.mainClass} + + + + ${shade.mainClass} + + + + + + + + *:* + + + META-INF/*.MF + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java index e71cf9afe4..a7326a7ea6 100644 --- a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java +++ b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java @@ -86,6 +86,8 @@ public static void main(String[] args) throws IOException { } else { handleDPG(codeModel, emitterOptions, sdkIntegration, outputDir); } + // ensure the process exits as expected + System.exit(0); } private static void handleFluent(CodeModel codeModel, EmitterOptions emitterOptions, boolean sdkIntegration) { @@ -179,7 +181,6 @@ private static void handleDPG(CodeModel codeModel, EmitterOptions emitterOptions typeSpecPlugin.writeFile("src/main/resources/META-INF/" + artifactId + "_apiview_properties.json", sb.toString(), null); } - System.exit(0); } private static EmitterOptions loadEmitterOptions(CodeModel codeModel) { diff --git a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java index 34ef4db928..2b15e43907 100644 --- a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java +++ b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java @@ -212,6 +212,8 @@ public void write(int b) { // NO-OP } }, null); + // it's a mock connection, we don't need it to do anything + stop(); } }