Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Serializer for Units #1239

Open
TimLCondor opened this issue Apr 18, 2023 · 5 comments
Open

JSON Serializer for Units #1239

TimLCondor opened this issue Apr 18, 2023 · 5 comments

Comments

@TimLCondor
Copy link

Is your feature request related to a problem? Please describe.
As of now, it is possible to serialize IQuantities with JSON. However, sometimes there is a need to serialize a unit by itself without a value. I.e. in the case when you just want to save a setting that happens to be a unit type. Or in our case we would like to define a multi dimensional table of data and set the unit just once next to it, for readability purposes.

Describe the solution you'd like
A converter for Unit Enums. Or alternatively it would be enough to make
private Enum GetUnit(string unit)
in UnitsNetBaseJsonConverter protected, so that we can implement a converter by ourself.

Describe alternatives you've considered
We are currently using an IQuantity with arbritary values, just to save and extract the units. Kind of ugly, kind of confusing for people not familiar with this topic.

@angularsen
Copy link
Owner

I guess, you could just create your own JsonConverter<Enum> inspired by UnitsNetBaseJsonConverter?

If you are interested in attempting a pull request, then we could add this converter to the library for others to benefit from.

@angularsen
Copy link
Owner

Sorry for the late reply by the way, it got lost in the inbox.

@vKaras1337
Copy link
Contributor

I have some of the UnitsNet.Units enums in my setting classes like this example

public PressureUnit SensorUnit { get; set;} = PressureUnit.Bar;

add the Newtonsoft.Json.Converters.StringEnumConverter to my JsonSerializerSettings.Converters witch gives a nice

{
  "SensorUnit": "Bar"
}

hope that helps

@TimLCondor
Copy link
Author

@vKaras1337 that is a very nice solution, thanks for sharing.

However, you already know the physical unit you want to use. In that case "PressureUnit". In my case I don't know the unit beforehand, because my code reads in generic "tables". The entries of said table may be of any desired unit, be it Mass, Time or Pressure since it is a generic library to be used on top of UnitsNet.

And while Newtonsoft.Json.Converters.StringEnumConverter can serialize
Enum Unit = PressureUnit.Bar;
to
"Unit": "Bar"
it is unable to deserialize it again.

I did not have the time yet to revisit this topic properly as of now. Thanks for your suggestion though, I think this is helpful for a lot of people.

@vKaras1337
Copy link
Contributor

@TimLCondor Oh that is really something..

I was a little curious how I would solve this. Maybe you can use something of it.
(thats only a quick test, that converter would need some more to it, error handling, type checks, etc.)

var data = Newtonsoft.Json.JsonConvert.SerializeObject(new MyTestClass(), Newtonsoft.Json.Formatting.Indented).Dump();
Newtonsoft.Json.JsonConvert.DeserializeObject<MyTestClass>(data).Dump();

public class MyTestClass
{
	[JsonConverter(typeof(StringEnumConverter))]
	public PressureUnit UnitDirect { get; set; } = PressureUnit.Bar;

	[JsonConverter(typeof(UnitsNetEnumUnitConverter))]
	public Enum UnitGeneric { get; set; } = AreaUnit.SquareKilometer;
}

public class UnitsNetEnumUnitConverter : Newtonsoft.Json.JsonConverter
{
	public override bool CanConvert(Type objectType)
	{
		return objectType.IsEnum;
	}

	public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
	{
		if (reader.TokenType == JsonToken.Null)
		{
			return null;
		}

		if (reader.TokenType == JsonToken.String)
		{
			var splitValue = reader.Value!.ToString()!.Split('.');
			string typeName = splitValue[0];
			string enumName = splitValue[1];

			return Enum.Parse(Type.GetType($"UnitsNet.Units.{typeName}, UnitsNet")!, enumName);
		}

		throw new NotSupportedException();
	}

	public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
	{
		writer.WriteValue($"{value!.GetType().Name}.{Enum.GetName(value.GetType(), value)}");
	}
}

witch gives a

{
  "UnitDirect": "Bar",
  "UnitGeneric": "AreaUnit.SquareKilometer"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants