Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,118 @@ public void PolymorphicDeserializationConsidersOnlyOwnDerivedTypes()
Assert.Equal(zooReserialized, zooWithPrivateSetHalf1 + zooWithPrivateSetHalf2);
}

[Fact]
public void PolymorphicDeserializationConsidersAdditionalPropertiesBaseline()
{
// non-polymorhpic version of PolymorphicDeserializationConsidersAdditionalProperties,
// to validate that behavior tested for there actually matches non-polymorphic behavior

var deserializeSettings = new JsonSerializerSettings();
deserializeSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
deserializeSettings.NullValueHandling = NullValueHandling.Ignore;

string animalsWithAdditionalProperties = @"[
{
""dType"": ""dog"",
""likesDogfood"": true,
""name"": ""Fido"",
""favoriteDogfood"": ""cats""
},
{
""dType"": ""cat"",
""likesMice"": false,
""dislikes"": {
""dType"": ""dog"",
""likesDogfood"": true,
""name"": ""Angry""
},
""name"": ""Felix"",
""likesSquirrels"": false,
""likesCowbell"": 42
},
{
""dType"": ""siamese"",
""color"": ""grey"",
""likesMice"": false,
""name"": ""Felix"",
""likesSquirrels"": true
}
]";

// see if it round trips
var tmpAnimals = JsonConvert.DeserializeObject<Animal[]>(animalsWithAdditionalProperties, deserializeSettings);
var serializeSettings = new JsonSerializerSettings();
serializeSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
serializeSettings.NullValueHandling = NullValueHandling.Ignore;
serializeSettings.Formatting = Formatting.Indented;
animalsWithAdditionalProperties = JsonConvert.SerializeObject(tmpAnimals, serializeSettings);

// deserialize and check
var animals = JsonConvert.DeserializeObject<Animal[]>(animalsWithAdditionalProperties, deserializeSettings);

Assert.Equal("cats", animals[0].AdditionalProperties["favoriteDogfood"]);
Assert.Equal(false, animals[1].AdditionalProperties["likesSquirrels"]);
Assert.Equal(true, animals[2].AdditionalProperties["likesSquirrels"]);
}

[Fact]
public void PolymorphicDeserializationConsidersAdditionalProperties()
{
var deserializeSettings = new JsonSerializerSettings();
deserializeSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
deserializeSettings.NullValueHandling = NullValueHandling.Ignore;
deserializeSettings.Converters.Add(new PolymorphicDeserializeJsonConverter<Animal>("dType"));

string animalsWithAdditionalProperties = @"[
{
""dType"": ""dog"",
""likesDogfood"": true,
""name"": ""Fido"",
""favoriteDogfood"": ""cats""
},
{
""dType"": ""cat"",
""likesMice"": false,
""dislikes"": {
""dType"": ""dog"",
""likesDogfood"": true,
""name"": ""Angry""
},
""name"": ""Felix"",
""likesSquirrels"": false,
""likesCowbell"": 42
},
{
""dType"": ""siamese"",
""color"": ""grey"",
""likesMice"": false,
""name"": ""Felix"",
""likesSquirrels"": true
}
]";

// see if it round trips
var tmpAnimals = JsonConvert.DeserializeObject<Animal[]>(animalsWithAdditionalProperties, deserializeSettings);
var serializeSettings = new JsonSerializerSettings();
serializeSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
serializeSettings.NullValueHandling = NullValueHandling.Ignore;
serializeSettings.Formatting = Formatting.Indented;
serializeSettings.Converters.Add(new PolymorphicSerializeJsonConverter<Animal>("dType"));
animalsWithAdditionalProperties = JsonConvert.SerializeObject(tmpAnimals, serializeSettings);

// deserialize and check
var animals = JsonConvert.DeserializeObject<Animal[]>(animalsWithAdditionalProperties, deserializeSettings);

Assert.Equal("cats", animals[0].AdditionalProperties["favoriteDogfood"]);
Assert.Equal(false, animals[1].AdditionalProperties["likesSquirrels"]);
Assert.Equal(true, animals[2].AdditionalProperties["likesSquirrels"]);
Assert.Equal(true, (animals[2] as Siamese).AdditionalProperties2["likesSquirrels"]);
Assert.Equal(1, animals[0].AdditionalProperties.Count);
Assert.Equal(2, animals[1].AdditionalProperties.Count);
Assert.Equal(1, animals[2].AdditionalProperties.Count);
Assert.Equal(1, (animals[2] as Siamese).AdditionalProperties2.Count);
}

private static void TestWithBadJsonSerializerSettings(Action callback)
{
Func<JsonSerializerSettings> oldDefault = JsonConvert.DefaultSettings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public class Animal

[JsonProperty("name")]
public string Name { get; set; }

[JsonExtensionData]
public IDictionary<string, object> AdditionalProperties { get; set; }
}

[JsonObject("dog")]
Expand Down Expand Up @@ -45,6 +48,9 @@ public class Siamese : Cat
{
[JsonProperty("color")]
public string Color { get; private set; }

[JsonExtensionData]
public IDictionary<string, object> AdditionalProperties2 { get; set; }
}

[JsonObject("alien")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
Expand Down Expand Up @@ -78,6 +79,16 @@ public static void SerializeProperties(JsonWriter writer, object value, JsonSeri
writer.WritePropertyName(propertyName);
serializer.Serialize(writer, memberValue);
}

// serialize additional properties
if (property.IsJsonExtensionData() && memberValue is IDictionary<string, object> additionalProperties)
{
foreach (var additionalProperty in additionalProperties)
{
writer.WritePropertyName(additionalProperty.Key);
serializer.Serialize(writer, additionalProperty.Value);
}
}
}
}

Expand All @@ -104,5 +115,8 @@ public static string GetPropertyName(this JsonProperty property, out string[] pa

return propertyName;
}

public static bool IsJsonExtensionData(this JsonProperty property)
=> property.AttributeProvider.GetAttributes(typeof(JsonExtensionDataAttribute), true).Count != 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.Rest.Serialization
Expand Down Expand Up @@ -91,14 +93,38 @@ public override object ReadJson(JsonReader reader,
var result = contract.DefaultCreator();

// parse properties
var queriedKeys = new HashSet<string> { Discriminator };
var additionalPropertiesTargets = new List<JsonProperty>();
foreach (var expectedProperty in contract.Properties)
{
var property = SelectTokenCaseInsensitive(item, expectedProperty.PropertyName);
if (property != null)
if (!expectedProperty.Ignored)
{
var propertyValue = property.ToObject(expectedProperty.PropertyType, serializer);
expectedProperty.ValueProvider.SetValue(result, propertyValue);
queriedKeys.Add(expectedProperty.PropertyName);
var property = SelectTokenCaseInsensitive(item, expectedProperty.PropertyName);
if (property != null)
{
var propertyValue = property.ToObject(expectedProperty.PropertyType, serializer);
expectedProperty.ValueProvider.SetValue(result, propertyValue);
}
}
if (expectedProperty.IsJsonExtensionData())
{
additionalPropertiesTargets.Add(expectedProperty);
}
}

// populate additional properties
var dict = new Dictionary<string, object>();
foreach (var property in item.Properties())
{
if (!queriedKeys.Contains(property.Name))
{
dict[property.Name] = property.Value.ToObject<object>();
}
}
foreach (var additionalPropertiesTarget in additionalPropertiesTargets)
{
additionalPropertiesTarget.ValueProvider.SetValue(result, dict);
}

return result;
Expand Down
14 changes: 8 additions & 6 deletions tools/generate.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ if not "%req_help%" == "" (
echo Usage: generate.cmd
echo ^<service, e.g. 'network/resource-manager'^>
echo ^<AutoRest version, defaults to 'latest'^>
echo ^<GitHub user of azure-rest-api-specs repo, defaults to 'Azure'^>
echo ^<Branch or commit ID of azure-rest-api-specs repo, defaults to 'current'^>
echo ^<GitHub user of specs repo, defaults to 'Azure'^>
echo ^<Branch or commit ID of specs repo, defaults to 'current'^>
echo ^<actual name of specs repo, defaults to 'azure-rest-api-specs'^>
echo.
echo Example: generate.cmd monitor/data-plane 1.1.0 olydis new-cool-feature
echo Example: generate.cmd monitor/data-plane 1.2.2 olydis new-cool-feature azure-rest-api-specs-pr
echo Note: If you are calling an SDK's generate.cmd, the first parameter is already provided for you.
echo.
echo To display this help, run either of
Expand All @@ -46,18 +47,19 @@ set rp="%1"
if not "%2" == "" (set version="%2") else (set version="latest")
if not "%3" == "" (set specsRepoUser="%3") else (set specsRepoUser="Azure")
if not "%4" == "" (set specsRepoBranch="%4") else (set specsRepoBranch="current")
set configFile="https://github.com/%specsRepoUser%/azure-rest-api-specs/blob/%specsRepoBranch%/specification/%rp%/readme.md"
if not "%5" == "" (set specsRepoName="%5") else (set specsRepoName="azure-rest-api-specs")
set configFile="https://github.com/%specsRepoUser%/%specsRepoName%/blob/%specsRepoBranch%/specification/%rp%/readme.md"

:: installation
if "%5" == "" (call npm i -g autorest)
if "%6" == "" (call npm i -g autorest)

:: code generation
@echo on
call autorest %configFile% --csharp --csharp-sdks-folder=%~dp0\..\src\SDKs --version=%version%
@echo off

:: metadata
mkdir %~dp0\..\src\SDKs\_metadata
mkdir %~dp0\..\src\SDKs\_metadata >nul 2>&1
call powershell %~dp0\generateMetadata.ps1 %specsRepoUser% %specsRepoBranch% %version% %configFile% > %~dp0\..\src\SDKs\_metadata\%rp:/=_%.txt

endlocal