UnitsNet icon indicating copy to clipboard operation
UnitsNet copied to clipboard

JSON Serializer for Units

Open TimLCondor opened this issue 2 years ago • 5 comments

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.

TimLCondor avatar Apr 18 '23 12:04 TimLCondor

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 avatar May 27 '23 00:05 angularsen

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

angularsen avatar May 27 '23 00:05 angularsen

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

vKaras1337 avatar Jun 21 '23 15:06 vKaras1337

@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.

TimLCondor avatar Jun 23 '23 12:06 TimLCondor

@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"
}

vKaras1337 avatar Jun 27 '23 14:06 vKaras1337

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Jul 08 '24 18:07 github-actions[bot]

This issue was automatically closed due to inactivity.

github-actions[bot] avatar Jul 16 '24 02:07 github-actions[bot]