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 . Collections . Concurrent ;
5
6
using System . Globalization ;
6
7
using System . Linq ;
7
8
using JetBrains . Annotations ;
@@ -17,6 +18,29 @@ namespace UnitsNet.Serialization.JsonNet
17
18
/// <typeparam name="T">The type being converted. Should either be <see cref="IQuantity"/> or <see cref="IComparable"/></typeparam>
18
19
public abstract class UnitsNetBaseJsonConverter < T > : JsonConverter < T >
19
20
{
21
+ private ConcurrentDictionary < string , ( Type Quantity , Type Unit ) > _registeredTypes = new ( ) ;
22
+
23
+ /// <summary>
24
+ /// Register custom types so that the converter can instantiate these quantities.
25
+ /// Instead of calling <see cref="Quantity.From"/>, the <see cref="Activator"/> will be used to instantiate the object.
26
+ /// It is therefore assumed that the constructor of <paramref name="quantity"/> is specified with <c>new T(double value, typeof(<paramref name="unit"/>) unit)</c>.
27
+ /// Registering the same <paramref name="unit"/> multiple times, it will overwrite the one registered.
28
+ /// </summary>
29
+ public void RegisterCustomType ( Type quantity , Type unit )
30
+ {
31
+ if ( ! typeof ( T ) . IsAssignableFrom ( quantity ) )
32
+ {
33
+ throw new ArgumentException ( $ "The type { quantity } is not a { typeof ( T ) } ") ;
34
+ }
35
+
36
+ if ( ! typeof ( Enum ) . IsAssignableFrom ( unit ) )
37
+ {
38
+ throw new ArgumentException ( $ "The type { unit } is not a { nameof ( Enum ) } ") ;
39
+ }
40
+
41
+ _registeredTypes [ unit . Name ] = ( quantity , unit ) ;
42
+ }
43
+
20
44
/// <summary>
21
45
/// Reads the "Unit" and "Value" properties from a JSON string
22
46
/// </summary>
@@ -79,6 +103,12 @@ protected IQuantity ConvertValueUnit(ValueUnit valueUnit)
79
103
}
80
104
81
105
var unit = GetUnit ( valueUnit . Unit ) ;
106
+ var registeredQuantity = GetRegisteredType ( valueUnit . Unit ) . Quantity ;
107
+
108
+ if ( registeredQuantity is not null )
109
+ {
110
+ return ( IQuantity ) Activator . CreateInstance ( registeredQuantity , valueUnit . Value , unit ) ;
111
+ }
82
112
83
113
return valueUnit switch
84
114
{
@@ -87,7 +117,45 @@ protected IQuantity ConvertValueUnit(ValueUnit valueUnit)
87
117
} ;
88
118
}
89
119
90
- private static Enum GetUnit ( string unit )
120
+ private ( Type Quantity , Type Unit ) GetRegisteredType ( string unit )
121
+ {
122
+ ( var unitEnumTypeName , var _ ) = SplitUnitString ( unit ) ;
123
+ if ( _registeredTypes . TryGetValue ( unitEnumTypeName , out var registeredType ) )
124
+ {
125
+ return registeredType ;
126
+ }
127
+
128
+ return ( null , null ) ;
129
+ }
130
+
131
+ private Enum GetUnit ( string unit )
132
+ {
133
+ ( var unitEnumTypeName , var unitEnumValue ) = SplitUnitString ( unit ) ;
134
+
135
+ // First try to find the name in the list of registered types.
136
+ var unitEnumType = GetRegisteredType ( unit ) . Unit ;
137
+
138
+ if ( unitEnumType is null )
139
+ {
140
+ // "UnitsNet.Units.MassUnit,UnitsNet"
141
+ var unitEnumTypeAssemblyQualifiedName = "UnitsNet.Units." + unitEnumTypeName + ",UnitsNet" ;
142
+
143
+ // -- see http://stackoverflow.com/a/6465096/1256096 for details
144
+ unitEnumType = Type . GetType ( unitEnumTypeAssemblyQualifiedName ) ;
145
+
146
+ if ( unitEnumType is null )
147
+ {
148
+ var ex = new UnitsNetException ( "Unable to find enum type." ) ;
149
+ ex . Data [ "type" ] = unitEnumTypeAssemblyQualifiedName ;
150
+ throw ex ;
151
+ }
152
+ }
153
+
154
+ var unitValue = ( Enum ) Enum . Parse ( unitEnumType , unitEnumValue ) ; // Ex: MassUnit.Kilogram
155
+ return unitValue ;
156
+ }
157
+
158
+ private static ( string EnumName , string EnumValue ) SplitUnitString ( string unit )
91
159
{
92
160
var unitParts = unit . Split ( '.' ) ;
93
161
@@ -99,23 +167,7 @@ private static Enum GetUnit(string unit)
99
167
}
100
168
101
169
// "MassUnit.Kilogram" => "MassUnit" and "Kilogram"
102
- var unitEnumTypeName = unitParts [ 0 ] ;
103
- var unitEnumValue = unitParts [ 1 ] ;
104
-
105
- // "UnitsNet.Units.MassUnit,UnitsNet"
106
- var unitEnumTypeAssemblyQualifiedName = "UnitsNet.Units." + unitEnumTypeName + ",UnitsNet" ;
107
-
108
- // -- see http://stackoverflow.com/a/6465096/1256096 for details
109
- var unitEnumType = Type . GetType ( unitEnumTypeAssemblyQualifiedName ) ;
110
- if ( unitEnumType == null )
111
- {
112
- var ex = new UnitsNetException ( "Unable to find enum type." ) ;
113
- ex . Data [ "type" ] = unitEnumTypeAssemblyQualifiedName ;
114
- throw ex ;
115
- }
116
-
117
- var unitValue = ( Enum ) Enum . Parse ( unitEnumType , unitEnumValue ) ; // Ex: MassUnit.Kilogram
118
- return unitValue ;
170
+ return ( unitParts [ 0 ] , unitParts [ 1 ] ) ;
119
171
}
120
172
121
173
/// <summary>
0 commit comments