diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs
index 081a702784..50de04d529 100644
--- a/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs
+++ b/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs
@@ -2,13 +2,9 @@
// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
using System;
-using System.Linq;
-using System.Reflection;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-using UnitsNet.Serialization.JsonNet.Internal;
-using UnitsNet.Units;
namespace UnitsNet.Serialization.JsonNet
{
@@ -26,11 +22,6 @@ namespace UnitsNet.Serialization.JsonNet
///
public class UnitsNetJsonConverter : JsonConverter
{
- ///
- /// Numeric value field of a quantity, typically of type double or decimal.
- ///
- private const string ValueFieldName = "_value";
-
///
/// Reads the JSON representation of the object.
///
@@ -46,29 +37,20 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
JsonSerializer serializer)
{
if (reader.ValueType != null)
- {
return reader.Value;
- }
+
object obj = TryDeserializeIComparable(reader, serializer);
// A null System.Nullable value or a comparable type was deserialized so return this
if (!(obj is ValueUnit vu))
- {
return obj;
- }
// "MassUnit.Kilogram" => "MassUnit" and "Kilogram"
string unitEnumTypeName = vu.Unit.Split('.')[0];
string unitEnumValue = vu.Unit.Split('.')[1];
- // "MassUnit" => "Mass"
- string quantityTypeName = unitEnumTypeName.Substring(0, unitEnumTypeName.Length - "Unit".Length);
-
// "UnitsNet.Units.MassUnit,UnitsNet"
string unitEnumTypeAssemblyQualifiedName = "UnitsNet.Units." + unitEnumTypeName + ",UnitsNet";
- // "UnitsNet.Mass,UnitsNet"
- string quantityTypeAssemblyQualifiedName = "UnitsNet." + quantityTypeName + ",UnitsNet";
-
// -- see http://stackoverflow.com/a/6465096/1256096 for details
Type unitEnumType = Type.GetType(unitEnumTypeAssemblyQualifiedName);
if (unitEnumType == null)
@@ -78,63 +60,10 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
throw ex;
}
- Type quantityType = Type.GetType(quantityTypeAssemblyQualifiedName);
- if (quantityType == null)
- {
- var ex = new UnitsNetException("Unable to find unit type.");
- ex.Data["type"] = quantityTypeAssemblyQualifiedName;
- throw ex;
- }
-
double value = vu.Value;
- object unitValue = Enum.Parse(unitEnumType, unitEnumValue); // Ex: MassUnit.Kilogram
-
- return CreateQuantity(quantityType, value, unitValue);
- }
-
- ///
- /// Creates a quantity (ex: Mass) based on the reflected quantity type, a numeric value and a unit value (ex: MassUnit.Kilogram).
- ///
- /// Type of quantity, such as .
- /// Numeric value.
- /// The unit, such as .
- /// The constructed quantity, such as .
- private static object CreateQuantity(Type quantityType, double value, object unitValue)
- {
- // We want the non-nullable return type, example candidates if quantity type is Mass:
- // double Mass.From(double, MassUnit)
- // double? Mass.From(double?, MassUnit)
- MethodInfo notNullableFromMethod = quantityType
- .GetDeclaredMethods()
- .Single(m => m.Name == "From" && Nullable.GetUnderlyingType(m.ReturnType) == null);
-
- // Of type QuantityValue
- object quantityValue = GetFromMethodValueArgument(notNullableFromMethod, value);
-
- // Ex: Mass.From(55, MassUnit.Gram)
- // See ValueUnit about precision loss for quantities using decimal type.
- return notNullableFromMethod.Invoke(null, new[] {quantityValue, unitValue});
- }
-
- ///
- /// Returns numeric value wrapped as , based on the type of argument
- /// of . Today this is always , but
- /// we may extend to other types later such as QuantityValueDecimal.
- ///
- /// The reflected From(value, unit) method.
- /// The value to convert to the correct wrapper type.
- ///
- private static object GetFromMethodValueArgument(MethodInfo fromMethod, double value)
- {
- Type valueParameterType = fromMethod.GetParameters()[0].ParameterType;
- if (valueParameterType == typeof(QuantityValue))
- {
- // We use this type that takes implicit cast from all number types to avoid explosion of method overloads that take a numeric value.
- return (QuantityValue) value;
- }
+ Enum unitValue = (Enum)Enum.Parse(unitEnumType, unitEnumValue); // Ex: MassUnit.Kilogram
- throw new Exception(
- $"The first parameter of the reflected quantity From() method was expected to be either UnitsNet.QuantityValue, but was instead {valueParameterType}.");
+ return Quantity.From(value, unitValue);
}
private static object TryDeserializeIComparable(JsonReader reader, JsonSerializer serializer)
@@ -182,74 +111,14 @@ public override void WriteJson(JsonWriter writer, object obj, JsonSerializer ser
return;
}
- object quantityValue = GetValueOfQuantity(obj, quantityType); // double or decimal value
- string quantityUnitName = GetUnitFullNameOfQuantity(obj, quantityType); // Example: "MassUnit.Kilogram"
+ IQuantity quantity = obj as IQuantity;
serializer.Serialize(writer, new ValueUnit
{
// See ValueUnit about precision loss for quantities using decimal type.
- Value = Convert.ToDouble(quantityValue),
- Unit = quantityUnitName
- });
- }
-
- ///
- /// Given quantity (ex: ), returns the full name (ex: "MassUnit.Kilogram") of the constructed unit given by the property.
- ///
- /// Quantity, such as .
- /// The type of , passed in here to reuse a previous lookup.
- /// "MassUnit.Kilogram" for a mass quantity whose Unit property is MassUnit.Kilogram.
- private static string GetUnitFullNameOfQuantity(object obj, Type quantityType)
- {
- // Get value of Unit property
- PropertyInfo unitProperty = quantityType.GetProperty("Unit");
- Enum quantityUnit = (Enum) unitProperty.GetValue(obj, null); // MassUnit.Kilogram
-
- Type unitType = quantityUnit.GetType(); // MassUnit
- return $"{unitType.Name}.{quantityUnit}"; // "MassUnit.Kilogram"
- }
-
- private static object GetValueOfQuantity(object value, Type quantityType)
- {
- FieldInfo valueField = GetPrivateInstanceField(quantityType, ValueFieldName);
-
- // See ValueUnit about precision loss for quantities using decimal type.
- object quantityValue = valueField.GetValue(value);
- return quantityValue;
- }
-
- private static FieldInfo GetPrivateInstanceField(Type quantityType, string fieldName)
- {
- FieldInfo baseValueField;
- try
- {
- baseValueField = quantityType
-#if (NETSTANDARD1_0)
- .GetTypeInfo()
- .DeclaredFields
- .Where(f => !f.IsPublic && !f.IsStatic)
-#else
- .GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
-#endif
- .SingleOrDefault(f => f.Name == fieldName);
- }
- catch (InvalidOperationException)
- {
- var ex = new UnitsNetException($"Expected exactly one private field named [{fieldName}], but found multiple.");
- ex.Data["type"] = quantityType;
- ex.Data["fieldName"] = fieldName;
- throw ex;
- }
-
- if (baseValueField == null)
- {
- var ex = new UnitsNetException("No private fields found in type.");
- ex.Data["type"] = quantityType;
- ex.Data["fieldName"] = fieldName;
- throw ex;
- }
-
- return baseValueField;
+ Value = quantity.Value,
+ Unit = $"{quantity.QuantityInfo.UnitType.Name}.{quantity.Unit}" // Example: "MassUnit.Kilogram"
+ } );
}
///
@@ -280,9 +149,7 @@ private class ValueUnit
public override bool CanConvert(Type objectType)
{
if (IsNullable(objectType))
- {
return CanConvertNullable(objectType);
- }
return objectType.Namespace != null &&
(objectType.Namespace.Equals(nameof(UnitsNet)) ||
@@ -314,6 +181,5 @@ protected virtual bool CanConvertNullable(Type objectType)
}
#endregion
-
}
}
diff --git a/UnitsNet.Tests/UnitConverterTest.cs b/UnitsNet.Tests/UnitConverterTest.cs
index db197fd945..4668ad351b 100644
--- a/UnitsNet.Tests/UnitConverterTest.cs
+++ b/UnitsNet.Tests/UnitConverterTest.cs
@@ -79,9 +79,9 @@ public void ConvertByAbbreviation_ConvertsTheValueToGivenUnit(double expectedVal
[Theory]
[InlineData(1, "UnknownQuantity", "m", "cm")]
- public void ConvertByAbbreviation_ThrowsQuantityNotFoundExceptionOnUnknownQuantity(double inputValue, string quantityTypeName, string fromUnit, string toUnit)
+ public void ConvertByAbbreviation_ThrowsUnitNotFoundExceptionOnUnknownQuantity( double inputValue, string quantityTypeName, string fromUnit, string toUnit)
{
- Assert.Throws(() => UnitConverter.ConvertByAbbreviation(inputValue, quantityTypeName, fromUnit, toUnit));
+ Assert.Throws(() => UnitConverter.ConvertByAbbreviation(inputValue, quantityTypeName, fromUnit, toUnit));
}
[Theory]
diff --git a/UnitsNet/QuantityNotFoundException.cs b/UnitsNet/QuantityNotFoundException.cs
index bda26cd584..78ce500b9e 100644
--- a/UnitsNet/QuantityNotFoundException.cs
+++ b/UnitsNet/QuantityNotFoundException.cs
@@ -1,4 +1,4 @@
-// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
using System;
@@ -9,6 +9,7 @@ namespace UnitsNet
/// Quantity type was not found. This is typically thrown for dynamic conversions,
/// such as .
///
+ [Obsolete("")]
public class QuantityNotFoundException : UnitsNetException
{
///
diff --git a/UnitsNet/UnitConverter.cs b/UnitsNet/UnitConverter.cs
index e6d21c34b2..74f83c59ff 100644
--- a/UnitsNet/UnitConverter.cs
+++ b/UnitsNet/UnitConverter.cs
@@ -4,9 +4,7 @@
using System;
using System.Globalization;
using System.Linq;
-using System.Reflection;
using JetBrains.Annotations;
-using UnitsNet.InternalHelpers;
using UnitsNet.Units;
namespace UnitsNet
@@ -17,18 +15,6 @@ namespace UnitsNet
[PublicAPI]
public static class UnitConverter
{
- private static readonly string UnitTypeNamespace = typeof(LengthUnit).Namespace;
- private static readonly Assembly UnitsNetAssembly = typeof(Length).Wrap().Assembly;
-
- private static readonly Type[] QuantityTypes = UnitsNetAssembly.GetTypes()
- .Where(typeof(IQuantity).Wrap().IsAssignableFrom)
- .Where(x => x.Wrap().IsClass || x.Wrap().IsValueType) // Future-proofing: we are discussing changing quantities from struct to class
- .ToArray();
-
- private static readonly Type[] UnitTypes = UnitsNetAssembly.GetTypes()
- .Where(x => x.Namespace == UnitTypeNamespace && x.Wrap().IsEnum && x.Name.EndsWith("Unit"))
- .ToArray();
-
///
/// Convert between any two quantity units given a numeric value and two unit enum values.
///
@@ -54,12 +40,12 @@ public static double Convert(QuantityValue fromValue, Enum fromUnitValue, Enum t
public static bool TryConvert(QuantityValue fromValue, Enum fromUnitValue, Enum toUnitValue, out double convertedValue)
{
convertedValue = 0;
- if (!Quantity.TryFrom(fromValue, fromUnitValue, out IQuantity from)) return false;
+ if (!Quantity.TryFrom(fromValue, fromUnitValue, out IQuantity fromQuantity)) return false;
try
{
// We're not going to implement TryAs() in all quantities, so let's just try-catch here
- convertedValue = from.As(toUnitValue);
+ convertedValue = fromQuantity.As(toUnitValue);
return true;
}
catch
@@ -94,7 +80,6 @@ public static bool TryConvert(QuantityValue fromValue, Enum fromUnitValue, Enum
///
/// double centimeters = ConvertByName(5, "Length", "Meter", "Centimeter"); // 500
/// Output value as the result of converting to .
- /// No quantities were found that match .
/// No units match the abbreviation.
/// More than one unit matches the abbreviation.
public static double ConvertByName(QuantityValue fromValue, string quantityName, string fromUnit, string toUnit)
@@ -150,13 +135,13 @@ public static bool TryConvertByName(QuantityValue inputValue, string quantityNam
{
result = 0d;
- if (!TryGetUnitType(quantityName, out var unitType))
+ if (!TryGetUnitType(quantityName, out Type unitType))
return false;
- if (!TryParseUnit(unitType, fromUnit, out var fromUnitValue)) // ex: LengthUnit.Meter
+ if (!TryParseUnit(unitType, fromUnit, out Enum fromUnitValue)) // ex: LengthUnit.Meter
return false;
- if (!TryParseUnit(unitType, toUnit, out var toUnitValue)) // ex: LengthUnit.Centimeter
+ if (!TryParseUnit(unitType, toUnit, out Enum toUnitValue)) // ex: LengthUnit.Centimeter
return false;
result = Convert(inputValue, fromUnitValue, toUnitValue);
@@ -221,7 +206,6 @@ public static double ConvertByAbbreviation(QuantityValue fromValue, string quant
/// Culture to parse abbreviations with.
/// double centimeters = ConvertByName(5, "Length", "m", "cm"); // 500
/// Output value as the result of converting to .
- /// No quantity types match the .
///
/// No unit types match the prefix of or no units
/// are mapped to the abbreviation.
@@ -229,24 +213,16 @@ public static double ConvertByAbbreviation(QuantityValue fromValue, string quant
/// More than one unit matches the abbreviation.
public static double ConvertByAbbreviation(QuantityValue fromValue, string quantityName, string fromUnitAbbrev, string toUnitAbbrev, string culture)
{
- if (!TryGetQuantityType(quantityName, out var quantityType))
- throw new QuantityNotFoundException($"The given quantity name was not found: {quantityName}");
-
- if (!TryGetUnitType(quantityName, out var unitType))
+ if (!TryGetUnitType(quantityName, out Type unitType))
throw new UnitNotFoundException($"The unit type for the given quantity was not found: {quantityName}");
var cultureInfo = string.IsNullOrWhiteSpace(culture) ? GlobalConfiguration.DefaultCulture : new CultureInfo(culture);
- var fromUnitValue = UnitParser.Default.Parse(fromUnitAbbrev, unitType, cultureInfo); // ex: ("m", LengthUnit) => LengthUnit.Meter
- var toUnitValue = UnitParser.Default.Parse(toUnitAbbrev, unitType, cultureInfo); // ex:("cm", LengthUnit) => LengthUnit.Centimeter
-
- var fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit)
- var fromResult = fromMethod.Invoke(null, new object[] {fromValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter)
+ var fromUnit = UnitParser.Default.Parse(fromUnitAbbrev, unitType, cultureInfo); // ex: ("m", LengthUnit) => LengthUnit.Meter
+ var fromQuantity = Quantity.From(fromValue, fromUnit);
- var asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit)
- var asResult = asMethod.Invoke(fromResult, new object[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter)
-
- return (double) asResult;
+ var toUnit = UnitParser.Default.Parse(toUnitAbbrev, unitType, cultureInfo); // ex:("cm", LengthUnit) => LengthUnit.Centimeter
+ return fromQuantity.As(toUnit);
}
///
@@ -314,72 +290,23 @@ public static bool TryConvertByAbbreviation(QuantityValue fromValue, string quan
{
result = 0d;
- if (!TryGetQuantityType(quantityName, out var quantityType))
- return false;
-
- if (!TryGetUnitType(quantityName, out var unitType))
+ if (!TryGetUnitType(quantityName, out Type unitType))
return false;
var cultureInfo = string.IsNullOrWhiteSpace(culture) ? GlobalConfiguration.DefaultCulture : new CultureInfo(culture);
- if (!UnitParser.Default.TryParse(fromUnitAbbrev, unitType, cultureInfo, out var fromUnitValue)) // ex: ("m", LengthUnit) => LengthUnit.Meter
+ if (!UnitParser.Default.TryParse(fromUnitAbbrev, unitType, cultureInfo, out Enum fromUnit)) // ex: ("m", LengthUnit) => LengthUnit.Meter
return false;
- if (!UnitParser.Default.TryParse(toUnitAbbrev, unitType, cultureInfo, out var toUnitValue)) // ex:("cm", LengthUnit) => LengthUnit.Centimeter
+ if (!UnitParser.Default.TryParse(toUnitAbbrev, unitType, cultureInfo, out Enum toUnit)) // ex:("cm", LengthUnit) => LengthUnit.Centimeter
return false;
- var fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit)
- var fromResult = fromMethod.Invoke(null, new object[] {fromValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter)
-
- var asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit)
- var asResult = asMethod.Invoke(fromResult, new object[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter)
-
- result = (double) asResult;
- return true;
- }
-
- private static MethodInfo GetAsMethod(Type quantityType, Type unitType)
- {
- // Only a single As() method as of this writing, but let's safe-guard a bit for future-proofing
- // ex: double result = quantity.As(LengthUnit outputUnit);
- return quantityType.Wrap().GetDeclaredMethods()
- .Single(m => m.Name == "As" &&
- !m.IsStatic &&
- m.IsPublic &&
- HasParameterTypes(m, unitType) &&
- m.ReturnType == typeof(double));
- }
-
- private static MethodInfo GetStaticFromMethod(Type quantityType, Type unitType)
- {
- // Want to match: Length l = UnitsNet.Length.From(double inputValue, LengthUnit inputUnit)
- // Do NOT match : Length? UnitsNet.Length.From(double? inputValue, LengthUnit inputUnit)
- return quantityType.Wrap().GetDeclaredMethods()
- .Single(m => m.Name == "From" &&
- m.IsStatic &&
- m.IsPublic &&
- HasParameterTypes(m, typeof(QuantityValue), unitType) &&
- m.ReturnType == quantityType);
- }
-
- private static bool HasParameterTypes(MethodInfo methodInfo, params Type[] expectedTypes)
- {
- var parameters = methodInfo.GetParameters();
- if (parameters.Length != expectedTypes.Length)
- throw new ArgumentException($"The number of parameters {parameters.Length} did not match the number of types {expectedTypes.Length}.");
-
- for (var i = 0; i < parameters.Length; i++)
- {
- var p = parameters[i];
- var t = expectedTypes[i];
- if (p.ParameterType != t)
- return false;
- }
+ var fromQuantity = Quantity.From(fromValue, fromUnit);
+ result = fromQuantity.As(toUnit);
return true;
}
-
///
/// Parse a unit by the unit enum type and a unit enum value >
///
@@ -402,19 +329,10 @@ private static bool TryParseUnit(Type unitType, string unitName, out Enum unitVa
private static bool TryGetUnitType(string quantityName, out Type unitType)
{
- var unitTypeName = quantityName + "Unit"; // ex. LengthUnit
-
- unitType = UnitTypes.FirstOrDefault(x =>
- x.Name.Equals(unitTypeName, StringComparison.OrdinalIgnoreCase));
-
- return unitType != null;
- }
-
- private static bool TryGetQuantityType(string quantityName, out Type quantityType)
- {
- quantityType = QuantityTypes.FirstOrDefault(x => x.Name.Equals(quantityName, StringComparison.OrdinalIgnoreCase));
+ var quantityInfo = Quantity.Infos.FirstOrDefault((info) => info.Name.Equals(quantityName, StringComparison.OrdinalIgnoreCase));
- return quantityType != null;
+ unitType = quantityInfo?.UnitType;
+ return quantityInfo != null;
}
}
}