2
2
// Copyright 2013 Andreas Gullberg Larsen ([email protected] ). Maintained at https://github.com/angularsen/UnitsNet.
3
3
4
4
using System ;
5
- using System . Linq ;
6
- using System . Reflection ;
7
5
using JetBrains . Annotations ;
8
6
using Newtonsoft . Json ;
9
7
using Newtonsoft . Json . Linq ;
10
- using UnitsNet . Serialization . JsonNet . Internal ;
11
- using UnitsNet . Units ;
12
8
13
9
namespace UnitsNet . Serialization . JsonNet
14
10
{
@@ -26,11 +22,6 @@ namespace UnitsNet.Serialization.JsonNet
26
22
/// </remarks>
27
23
public class UnitsNetJsonConverter : JsonConverter
28
24
{
29
- /// <summary>
30
- /// Numeric value field of a quantity, typically of type double or decimal.
31
- /// </summary>
32
- private const string ValueFieldName = "_value" ;
33
-
34
25
/// <summary>
35
26
/// Reads the JSON representation of the object.
36
27
/// </summary>
@@ -46,29 +37,20 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
46
37
JsonSerializer serializer )
47
38
{
48
39
if ( reader . ValueType != null )
49
- {
50
40
return reader . Value ;
51
- }
41
+
52
42
object obj = TryDeserializeIComparable ( reader , serializer ) ;
53
43
// A null System.Nullable value or a comparable type was deserialized so return this
54
44
if ( ! ( obj is ValueUnit vu ) )
55
- {
56
45
return obj ;
57
- }
58
46
59
47
// "MassUnit.Kilogram" => "MassUnit" and "Kilogram"
60
48
string unitEnumTypeName = vu . Unit . Split ( '.' ) [ 0 ] ;
61
49
string unitEnumValue = vu . Unit . Split ( '.' ) [ 1 ] ;
62
50
63
- // "MassUnit" => "Mass"
64
- string quantityTypeName = unitEnumTypeName . Substring ( 0 , unitEnumTypeName . Length - "Unit" . Length ) ;
65
-
66
51
// "UnitsNet.Units.MassUnit,UnitsNet"
67
52
string unitEnumTypeAssemblyQualifiedName = "UnitsNet.Units." + unitEnumTypeName + ",UnitsNet" ;
68
53
69
- // "UnitsNet.Mass,UnitsNet"
70
- string quantityTypeAssemblyQualifiedName = "UnitsNet." + quantityTypeName + ",UnitsNet" ;
71
-
72
54
// -- see http://stackoverflow.com/a/6465096/1256096 for details
73
55
Type unitEnumType = Type . GetType ( unitEnumTypeAssemblyQualifiedName ) ;
74
56
if ( unitEnumType == null )
@@ -78,63 +60,10 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
78
60
throw ex ;
79
61
}
80
62
81
- Type quantityType = Type . GetType ( quantityTypeAssemblyQualifiedName ) ;
82
- if ( quantityType == null )
83
- {
84
- var ex = new UnitsNetException ( "Unable to find unit type." ) ;
85
- ex . Data [ "type" ] = quantityTypeAssemblyQualifiedName ;
86
- throw ex ;
87
- }
88
-
89
63
double value = vu . Value ;
90
- object unitValue = Enum . Parse ( unitEnumType , unitEnumValue ) ; // Ex: MassUnit.Kilogram
91
-
92
- return CreateQuantity ( quantityType , value , unitValue ) ;
93
- }
94
-
95
- /// <summary>
96
- /// Creates a quantity (ex: Mass) based on the reflected quantity type, a numeric value and a unit value (ex: MassUnit.Kilogram).
97
- /// </summary>
98
- /// <param name="quantityType">Type of quantity, such as <see cref="Mass"/>.</param>
99
- /// <param name="value">Numeric value.</param>
100
- /// <param name="unitValue">The unit, such as <see cref="MassUnit.Kilogram"/>.</param>
101
- /// <returns>The constructed quantity, such as <see cref="Mass"/>.</returns>
102
- private static object CreateQuantity ( Type quantityType , double value , object unitValue )
103
- {
104
- // We want the non-nullable return type, example candidates if quantity type is Mass:
105
- // double Mass.From(double, MassUnit)
106
- // double? Mass.From(double?, MassUnit)
107
- MethodInfo notNullableFromMethod = quantityType
108
- . GetDeclaredMethods ( )
109
- . Single ( m => m . Name == "From" && Nullable . GetUnderlyingType ( m . ReturnType ) == null ) ;
110
-
111
- // Of type QuantityValue
112
- object quantityValue = GetFromMethodValueArgument ( notNullableFromMethod , value ) ;
113
-
114
- // Ex: Mass.From(55, MassUnit.Gram)
115
- // See ValueUnit about precision loss for quantities using decimal type.
116
- return notNullableFromMethod . Invoke ( null , new [ ] { quantityValue , unitValue } ) ;
117
- }
118
-
119
- /// <summary>
120
- /// Returns numeric value wrapped as <see cref="QuantityValue"/>, based on the type of argument
121
- /// of <paramref name="fromMethod"/>. Today this is always <see cref="QuantityValue"/>, but
122
- /// we may extend to other types later such as QuantityValueDecimal.
123
- /// </summary>
124
- /// <param name="fromMethod">The reflected From(value, unit) method.</param>
125
- /// <param name="value">The value to convert to the correct wrapper type.</param>
126
- /// <returns></returns>
127
- private static object GetFromMethodValueArgument ( MethodInfo fromMethod , double value )
128
- {
129
- Type valueParameterType = fromMethod . GetParameters ( ) [ 0 ] . ParameterType ;
130
- if ( valueParameterType == typeof ( QuantityValue ) )
131
- {
132
- // We use this type that takes implicit cast from all number types to avoid explosion of method overloads that take a numeric value.
133
- return ( QuantityValue ) value ;
134
- }
64
+ Enum unitValue = ( Enum ) Enum . Parse ( unitEnumType , unitEnumValue ) ; // Ex: MassUnit.Kilogram
135
65
136
- throw new Exception (
137
- $ "The first parameter of the reflected quantity From() method was expected to be either UnitsNet.QuantityValue, but was instead { valueParameterType } .") ;
66
+ return Quantity . From ( value , unitValue ) ;
138
67
}
139
68
140
69
private static object TryDeserializeIComparable ( JsonReader reader , JsonSerializer serializer )
@@ -182,74 +111,14 @@ public override void WriteJson(JsonWriter writer, object obj, JsonSerializer ser
182
111
return ;
183
112
}
184
113
185
- object quantityValue = GetValueOfQuantity ( obj , quantityType ) ; // double or decimal value
186
- string quantityUnitName = GetUnitFullNameOfQuantity ( obj , quantityType ) ; // Example: "MassUnit.Kilogram"
114
+ IQuantity quantity = obj as IQuantity ;
187
115
188
116
serializer . Serialize ( writer , new ValueUnit
189
117
{
190
118
// See ValueUnit about precision loss for quantities using decimal type.
191
- Value = Convert . ToDouble ( quantityValue ) ,
192
- Unit = quantityUnitName
193
- } ) ;
194
- }
195
-
196
- /// <summary>
197
- /// Given quantity (ex: <see cref="Mass"/>), returns the full name (ex: "MassUnit.Kilogram") of the constructed unit given by the <see cref="Mass.Unit"/> property.
198
- /// </summary>
199
- /// <param name="obj">Quantity, such as <see cref="Mass"/>.</param>
200
- /// <param name="quantityType">The type of <paramref name="obj"/>, passed in here to reuse a previous lookup.</param>
201
- /// <returns>"MassUnit.Kilogram" for a mass quantity whose Unit property is MassUnit.Kilogram.</returns>
202
- private static string GetUnitFullNameOfQuantity ( object obj , Type quantityType )
203
- {
204
- // Get value of Unit property
205
- PropertyInfo unitProperty = quantityType . GetProperty ( "Unit" ) ;
206
- Enum quantityUnit = ( Enum ) unitProperty . GetValue ( obj , null ) ; // MassUnit.Kilogram
207
-
208
- Type unitType = quantityUnit . GetType ( ) ; // MassUnit
209
- return $ "{ unitType . Name } .{ quantityUnit } "; // "MassUnit.Kilogram"
210
- }
211
-
212
- private static object GetValueOfQuantity ( object value , Type quantityType )
213
- {
214
- FieldInfo valueField = GetPrivateInstanceField ( quantityType , ValueFieldName ) ;
215
-
216
- // See ValueUnit about precision loss for quantities using decimal type.
217
- object quantityValue = valueField . GetValue ( value ) ;
218
- return quantityValue ;
219
- }
220
-
221
- private static FieldInfo GetPrivateInstanceField ( Type quantityType , string fieldName )
222
- {
223
- FieldInfo baseValueField ;
224
- try
225
- {
226
- baseValueField = quantityType
227
- #if ( NETSTANDARD1_0 )
228
- . GetTypeInfo ( )
229
- . DeclaredFields
230
- . Where ( f => ! f . IsPublic && ! f . IsStatic )
231
- #else
232
- . GetFields ( BindingFlags . NonPublic | BindingFlags . Instance | BindingFlags . DeclaredOnly )
233
- #endif
234
- . SingleOrDefault ( f => f . Name == fieldName ) ;
235
- }
236
- catch ( InvalidOperationException )
237
- {
238
- var ex = new UnitsNetException ( $ "Expected exactly one private field named [{ fieldName } ], but found multiple.") ;
239
- ex . Data [ "type" ] = quantityType ;
240
- ex . Data [ "fieldName" ] = fieldName ;
241
- throw ex ;
242
- }
243
-
244
- if ( baseValueField == null )
245
- {
246
- var ex = new UnitsNetException ( "No private fields found in type." ) ;
247
- ex . Data [ "type" ] = quantityType ;
248
- ex . Data [ "fieldName" ] = fieldName ;
249
- throw ex ;
250
- }
251
-
252
- return baseValueField ;
119
+ Value = quantity . Value ,
120
+ Unit = $ "{ quantity . QuantityInfo . UnitType . Name } .{ quantity . Unit } " // Example: "MassUnit.Kilogram"
121
+ } ) ;
253
122
}
254
123
255
124
/// <summary>
@@ -280,9 +149,7 @@ private class ValueUnit
280
149
public override bool CanConvert ( Type objectType )
281
150
{
282
151
if ( IsNullable ( objectType ) )
283
- {
284
152
return CanConvertNullable ( objectType ) ;
285
- }
286
153
287
154
return objectType . Namespace != null &&
288
155
( objectType . Namespace . Equals ( nameof ( UnitsNet ) ) ||
@@ -314,6 +181,5 @@ protected virtual bool CanConvertNullable(Type objectType)
314
181
}
315
182
316
183
#endregion
317
-
318
184
}
319
185
}
0 commit comments