Skip to content

Support deserialization for Struct properties that implements IList<T> - #41223

Merged
layomia merged 5 commits into
dotnet:masterfrom
RaymondHuy:issue-1998
Sep 27, 2020
Merged

Support deserialization for Struct properties that implements IList<T>#41223
layomia merged 5 commits into
dotnet:masterfrom
RaymondHuy:issue-1998

Conversation

@RaymondHuy

@RaymondHuyRaymondHuy commented Aug 23, 2020

Copy link
Copy Markdown
Contributor

Fixes#1998
Add a new field into IListOfTConverter class to store the values to prevent unboxing when the TCollection is ValueType.

Add a new field into IListOfTConverter class to store the values to prevent unboxing when the TCollection is ValueType.
@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@dnfadmin

dnfadmin commented Aug 23, 2020

Copy link
Copy Markdown

CLA assistant check
All CLA requirements met.

@layomialayomia added this to the 6.0.0 milestone Aug 24, 2020
HuyLuong added 2 commits August 26, 2020 23:24
…ion<T>, IList
Use constraint type instead of TCollection to avoid unboxing when TCollection is ValueType.
…T>, IDictionary<TKey, TValue>, IDictionary
Use constraint type to cast to avoid unboxing when the target type is ValueType
@RaymondHuy

Copy link
Copy Markdown
ContributorAuthor

Hi @layomia@steveharter I have updated my PR to resolve your comments. Are there anything else I can do to get this PR approved ?

@RaymondHuyRaymondHuy reopened this Sep 5, 2020

@layomialayomia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the changes.

We also need to update the Add methods of the following converter types and add corresponding tests:

  • ICollectionOfTConverter
  • IDictionaryConverter
  • IDictionaryOfTKeyTValueConverter
  • IListConverter
  • IListOfTConverter
  • ISetOfTConverter

Modify the 'Add' method to use actual type casting.
@RaymondHuy

Copy link
Copy Markdown
ContributorAuthor

@layomia I have just updated my PR.
Here is my benchmark result: The first table is before the change and the second table is after the change.

BenchmarkDotNet=v0.12.1.1405-nightly, OS=Windows 10.0.18363.1082 (1909/November2019Update/19H2)
Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores
.NET Core SDK=5.0.100-preview.7.20366.6
[Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.36411, CoreFX 5.0.20.36411), X64 RyuJIT
Job-WVOCPJ : .NET Core 5.0 (CoreCLR 42.42.42.42424, CoreFX 42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable Toolchain=CoreRun IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 

ArrayList

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString49.54 μs0.439 μs0.389 μs49.55 μs48.71 μs50.26 μs4.89050.5869-30.29 KB
DeserializeFromUtf8Bytes50.77 μs0.409 μs0.363 μs50.74 μs50.38 μs51.67 μs4.89400.6117-30.29 KB
DeserializeFromStream48.72 μs0.374 μs0.350 μs48.78 μs48.16 μs49.33 μs4.87040.5845-30.36 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString47.89 μs0.524 μs0.490 μs47.91 μs47.07 μs48.71 μs4.77010.5724-30.29 KB
DeserializeFromUtf8Bytes48.35 μs0.420 μs0.372 μs48.38 μs47.57 μs49.03 μs4.77550.5731-30.29 KB
DeserializeFromStream48.71 μs0.375 μs0.351 μs48.82 μs47.71 μs49.08 μs4.81320.5776-30.36 KB

BinaryData

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString631.3 ns6.61 ns5.86 ns630.5 ns623.3 ns642.4 ns0.1691--1.05 KB
DeserializeFromUtf8Bytes494.2 ns6.63 ns6.21 ns494.9 ns483.8 ns505.7 ns0.1702--1.05 KB
DeserializeFromStream802.9 ns5.92 ns5.25 ns802.5 ns793.5 ns811.6 ns0.1805--1.12 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString613.5 ns5.62 ns4.98 ns614.1 ns604.5 ns621.6 ns0.1708--1.05 KB
DeserializeFromUtf8Bytes474.7 ns5.66 ns5.30 ns474.6 ns465.5 ns484.4 ns0.1700--1.05 KB
DeserializeFromStream797.8 ns8.74 ns8.18 ns798.8 ns785.3 ns811.6 ns0.1823--1.12 KB

Dictionary_string, string

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString21.51 μs0.140 μs0.124 μs21.51 μs21.32 μs21.73 μs4.06500.5081-25.27 KB
DeserializeFromUtf8Bytes21.27 μs0.082 μs0.072 μs21.26 μs21.16 μs21.41 μs4.06710.4980-25.27 KB
DeserializeFromStream23.27 μs0.309 μs0.289 μs23.17 μs22.88 μs23.92 μs4.09800.4657-25.34 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString21.77 μs0.277 μs0.259 μs21.71 μs21.36 μs22.22 μs4.04610.5165-25.27 KB
DeserializeFromUtf8Bytes21.25 μs0.312 μs0.292 μs21.26 μs20.83 μs21.82 μs4.10520.5027-25.27 KB
DeserializeFromStream22.78 μs0.129 μs0.121 μs22.78 μs22.61 μs23.06 μs4.12480.4484-25.34 KB

HashSet_String

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString12.47 μs0.109 μs0.096 μs12.48 μs12.33 μs12.64 μs2.39440.1955-14.87 KB
DeserializeFromUtf8Bytes12.30 μs0.104 μs0.093 μs12.33 μs12.13 μs12.43 μs2.41710.1973-14.87 KB
DeserializeFromStream13.76 μs0.171 μs0.160 μs13.79 μs13.49 μs14.02 μs2.42460.1616-14.94 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString12.80 μs0.077 μs0.072 μs12.82 μs12.67 μs12.96 μs2.41550.2013-14.87 KB
DeserializeFromUtf8Bytes11.90 μs0.113 μs0.106 μs11.87 μs11.73 μs12.12 μs2.38190.1905-14.87 KB
DeserializeFromStream13.69 μs0.102 μs0.096 μs13.69 μs13.52 μs13.82 μs2.42880.1619-14.94 KB

HashTable

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString64.86 μs0.419 μs0.371 μs64.95 μs64.28 μs65.30 μs7.06991.3092-44.22 KB
DeserializeFromUtf8Bytes65.87 μs0.531 μs0.497 μs65.79 μs64.76 μs66.66 μs7.01121.2984-44.22 KB
DeserializeFromStream66.66 μs0.435 μs0.385 μs66.60 μs66.08 μs67.32 μs7.14851.0590-44.29 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString65.16 μs0.761 μs0.712 μs64.97 μs64.18 μs66.29 μs7.12031.3186-44.22 KB
DeserializeFromUtf8Bytes66.34 μs0.585 μs0.547 μs66.47 μs65.38 μs67.23 μs7.11091.3168-44.22 KB
DeserializeFromStream65.35 μs0.448 μs0.397 μs65.37 μs64.51 μs66.08 μs7.00571.0379-44.29 KB

ImmutableDictionary_String, String

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString45.44 μs0.361 μs0.320 μs45.44 μs44.74 μs46.04 μs5.00740.5564-31.61 KB
DeserializeFromUtf8Bytes45.73 μs0.368 μs0.345 μs45.75 μs45.16 μs46.27 μs5.07250.7246-31.61 KB
DeserializeFromStream48.37 μs0.398 μs0.372 μs48.44 μs47.82 μs49.08 μs5.06330.5842-31.68 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString45.23 μs0.361 μs0.337 μs45.16 μs44.76 μs45.79 μs5.05780.5419-31.61 KB
DeserializeFromUtf8Bytes46.00 μs0.284 μs0.252 μs46.02 μs45.44 μs46.29 μs5.14710.7353-31.61 KB
DeserializeFromStream48.39 μs0.340 μs0.318 μs48.36 μs47.78 μs48.97 μs5.00960.5780-31.68 KB

ImmutableSortedDictionary_String, String

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString76.58 μs0.780 μs0.730 μs76.50 μs75.42 μs77.94 μs6.06800.6068-38.29 KB
DeserializeFromUtf8Bytes72.56 μs0.859 μs0.804 μs72.54 μs71.49 μs74.19 μs6.04840.8641-38.29 KB
DeserializeFromStream74.30 μs0.514 μs0.481 μs74.07 μs73.67 μs75.19 μs5.97010.5970-38.36 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString72.86 μs0.692 μs0.647 μs73.13 μs71.68 μs73.74 μs6.04840.8641-38.29 KB
DeserializeFromUtf8Bytes74.66 μs0.408 μs0.361 μs74.69 μs73.89 μs75.17 μs6.03860.6039-38.29 KB
DeserializeFromStream73.85 μs0.336 μs0.297 μs73.86 μs73.37 μs74.60 μs5.97190.5972-38.36 KB

IndexViewModel

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString28.99 μs0.241 μs0.213 μs28.97 μs28.60 μs29.40 μs3.57890.3463-22.01 KB
DeserializeFromUtf8Bytes28.33 μs0.226 μs0.200 μs28.32 μs28.02 μs28.76 μs3.48820.3376-22.01 KB
DeserializeFromStream31.05 μs0.374 μs0.350 μs31.01 μs30.55 μs31.62 μs3.49690.3747-22.08 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString29.42 μs0.273 μs0.242 μs29.42 μs29.00 μs29.86 μs3.54690.3547-22.01 KB
DeserializeFromUtf8Bytes28.02 μs0.172 μs0.144 μs28.02 μs27.74 μs28.24 μs3.51470.3401-22.01 KB
DeserializeFromStream30.78 μs0.261 μs0.244 μs30.70 μs30.42 μs31.18 μs3.55260.3675-22.08 KB

Int32

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString133.65 ns0.635 ns0.594 ns133.60 ns132.38 ns134.46 ns----
DeserializeFromUtf8Bytes81.61 ns0.301 ns0.281 ns81.59 ns81.09 ns81.96 ns----
DeserializeFromStream262.62 ns2.082 ns1.948 ns262.14 ns259.80 ns266.31 ns0.0107--72 B
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString136.19 ns0.877 ns0.821 ns136.16 ns134.82 ns137.77 ns----
DeserializeFromUtf8Bytes81.36 ns0.343 ns0.321 ns81.29 ns80.65 ns82.00 ns----
DeserializeFromStream253.83 ns2.359 ns2.207 ns253.43 ns250.68 ns257.52 ns0.0113--72 B

LargeStructWithProperties

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString1.142 μs0.0103 μs0.0091 μs1.140 μs1.127 μs1.160 μs0.0279--200 B
DeserializeFromUtf8Bytes1.069 μs0.0063 μs0.0059 μs1.069 μs1.059 μs1.081 μs0.0298--200 B
DeserializeFromStream1.596 μs0.0142 μs0.0132 μs1.590 μs1.581 μs1.620 μs0.0499--328 B
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString1.157 μs0.0076 μs0.0071 μs1.158 μs1.147 μs1.172 μs0.0276--200 B
DeserializeFromUtf8Bytes1.081 μs0.0065 μs0.0058 μs1.081 μs1.071 μs1.088 μs0.0302--200 B
DeserializeFromStream1.498 μs0.0094 μs0.0088 μs1.498 μs1.483 μs1.516 μs0.0475--328 B

Location

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString1.259 μs0.0092 μs0.0086 μs1.260 μs1.246 μs1.277 μs0.0698--448 B
DeserializeFromUtf8Bytes1.153 μs0.0112 μs0.0105 μs1.155 μs1.135 μs1.171 μs0.0689--448 B
DeserializeFromStream1.550 μs0.0104 μs0.0098 μs1.548 μs1.534 μs1.568 μs0.0815--520 B
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString1.240 μs0.0085 μs0.0079 μs1.240 μs1.221 μs1.253 μs0.0695--448 B
DeserializeFromUtf8Bytes1.130 μs0.0112 μs0.0105 μs1.133 μs1.109 μs1.143 μs0.0682--448 B
DeserializeFromStream1.534 μs0.0163 μs0.0153 μs1.538 μs1.512 μs1.563 μs0.0797--520 B

LoginViewModel

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString468.6 ns3.98 ns3.72 ns469.0 ns464.0 ns475.4 ns0.0261--168 B
DeserializeFromUtf8Bytes444.6 ns4.95 ns4.63 ns442.9 ns439.1 ns455.1 ns0.0255--168 B
DeserializeFromStream727.3 ns5.57 ns5.21 ns727.9 ns719.8 ns735.3 ns0.0375--240 B
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString464.8 ns1.69 ns1.58 ns464.9 ns462.0 ns467.4 ns0.0257--168 B
DeserializeFromUtf8Bytes451.0 ns3.38 ns3.16 ns451.4 ns443.2 ns454.8 ns0.0266--168 B
DeserializeFromStream702.5 ns4.70 ns4.17 ns701.3 ns696.4 ns710.7 ns0.0364--240 B

MyEventListerViewModel

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString324.4 μs2.10 μs1.97 μs324.2 μs320.7 μs328.0 μs11.61292.5806-76.9 KB
DeserializeFromUtf8Bytes304.2 μs1.40 μs1.24 μs304.2 μs302.5 μs306.6 μs12.13593.6408-76.9 KB
DeserializeFromStream363.7 μs1.48 μs1.24 μs363.6 μs361.9 μs366.4 μs11.57742.8944-77.74 KB
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString330.9 μs2.66 μs2.49 μs331.2 μs326.4 μs335.2 μs11.85772.6350-76.9 KB
DeserializeFromUtf8Bytes306.6 μs2.11 μs1.76 μs307.1 μs302.6 μs308.5 μs11.67322.5940-76.9 KB
DeserializeFromStream361.3 μs3.36 μs3.14 μs360.7 μs356.7 μs366.3 μs11.56072.8902-77.75 KB

SimpleStructWithProperties

MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString331.8 ns2.86 ns2.68 ns332.3 ns326.0 ns335.3 ns0.0092--64 B
DeserializeFromUtf8Bytes275.7 ns1.93 ns1.61 ns276.2 ns273.1 ns278.4 ns0.0091--64 B
DeserializeFromStream531.0 ns4.21 ns3.94 ns531.4 ns524.9 ns538.0 ns0.0212--144 B
MethodMeanErrorStdDevMedianMinMaxGen 0Gen 1Gen 2Allocated
DeserializeFromString332.5 ns2.25 ns1.88 ns332.3 ns329.2 ns335.4 ns0.0095--64 B
DeserializeFromUtf8Bytes299.5 ns1.80 ns1.68 ns299.1 ns297.1 ns302.1 ns0.0097--64 B
DeserializeFromStream517.6 ns5.21 ns4.87 ns516.2 ns510.3 ns528.9 ns0.0226--144 B

@layomialayomia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for sharing the benchmarks. The change is close to ready, I just have a few comments on the tests.

}

{
GenericStructIDictionaryWrapper<string, string>? obj = JsonSerializer.Deserialize<GenericStructIDictionaryWrapper<string, string>?>("null");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What happens when serializing a default instance of GenericStructIDictionaryWrapper<string, string>? Test this for GenericStructIListWrapper<int> as well.

- Serializing a default instance.
- Deserializing a null json string.
@RaymondHuy

Copy link
Copy Markdown
ContributorAuthor

@layomia Thanks for your comments. I've updated my PR, can you check if it is satisfied ?

@layomialayomia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, thanks!

@layomia
layomia merged commit 08472ef into dotnet:masterSep 27, 2020
@ghostghost locked as resolved and limited conversation to collaborators Dec 7, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Struct properties that implement IList<T> are unable to hold values on Deserialization

7 participants

@RaymondHuy@Dotnet-GitSync-Bot@dnfadmin@steveharter@layomia@devsko@jkotas