UnitsNet icon indicating copy to clipboard operation
UnitsNet copied to clipboard

Support Cross-Technologies DTO & API Specifications

Open haimkastner opened this issue 1 year ago • 0 comments

Is your feature request related to a problem? Please describe.

Request related

As part of the powerful and wide support in multiple languages in the unit definition, I think it can be cool to standardize the way how unit is represented in the API spec, and how it will be exposed/loaded.

Implementing in all UnitsNet-based libraries a similar API DTO to load/expose and the same JSON schema standard of how the unit is represented, will give the benefits of:

  1. Work with UnitsNet across various technologies transparently without any kind of manual conversions.
  2. Standart way how to represent units in API schemas.
  3. Clear OpenAPI specification (and automatically in case the spec is generated from the code object declarations)
  4. Human readable unit representation in the API schema, and easy to work with anyway.

See also haimkastner/unitsnet-js#29 for real-world, probably common use-case between C# backend and TypeScript frontend.

Currently, sterilizing is supported in the library, but not as a common, standard, unit dedicated and easy-to-use API.

Describe the solution you'd like

The JSON standart DTO will look like this:

{
   "value":100.01,
   "unit":"Meter"
}

As part of the full JSON API payload something like:

{
   "someInfo":"someValue",
   "someInfoLength":{
      "value":100.01,
      "unit":"Meter"
   }
}

See an OpenAPI unitsnet-openapi-spec example schema.

A basic and naive prototype of how it will be in C#

using System;
using UnitsNet;
using UnitsNet.Units;
using System.Text.Json;

public class LengthDto
{
	class JsonDto
	{
    	public double value { get; set; }
    	public string unit { get; set; }
	}
	
    public LengthDto(double value, LengthUnit unit)
    {
        this.value = value;
        this.unit = unit;
    }
	
    public double value { get; set; }
    public LengthUnit unit { get; set; }
	
	public string ToJson()
    {
		JsonDto jsonInterface = new JsonDto { value = this.value, unit = this.unit.ToString() };
		return JsonSerializer.Serialize(jsonInterface);
    }
	
	public static LengthDto fromJson(string json)
    {
		JsonDto jsonInterface = JsonSerializer.Deserialize<JsonDto>(json);
		Enum.TryParse<LengthUnit>(jsonInterface.unit, out LengthUnit newUnit);
		return new LengthDto(jsonInterface.value, newUnit);
    }
}


public static class LengthExtensions
{
    public static Length fromDto(this Length length, LengthDto lengthDto) 
    {
       return new Length(lengthDto.value, lengthDto.unit);
    }
	
	public static LengthDto toDto(this Length length, LengthUnit unit = LengthUnit.Meter) 
    {
       return new LengthDto(length.As(LengthUnit.Meter), unit);
    }
}

public class Program
{
	public static void Main()
	{
		Length lengthValue = Length.FromMeters(100.01);
		string json = lengthValue.toDto().ToJson();
		Console.WriteLine(json); // {"value":100.01,"unit":"Meter"}
		
		LengthDto newLengthDto = LengthDto.fromJson(json);
		Length newLengthValue = new Length().fromDto(newLengthDto);
		Console.WriteLine(newLengthValue.Meters); // 100.01
	}
}

A similar implementation I have created for the JS package haimkastner/unitsnet-js#32 & haimkastner/unitsnet-py#18

TypeScript usage (docs)

// Create a Length unit object
const length = Length.FromMeters(100.01);
// Obtain the DTO object, represented by the default unit - meter
const lengthDto: LengthDto = length.toDto(); // {"value":100.01,"unit":"Meter"}
// Obtain Length object from lengthDto
const newLength: Length = Length.FromDto(lengthDto);

Similar for the Python package (docs):

 # Create a Length unit object
length = Length.from_meters(100.01)
# Obtain the DTO object as json, represented by the default unit - meter
length_dto_json = length.to_dto_json() # {"value":100.01,"unit":"Meter"}
# Load JSON to DTO, and load
length_from_dto = Length.from_dto_json(length_dto_json)

Describe alternatives you've considered To do it manually.

Additional context

See an example of the Python Length implementation of the DTO class and the unit API example

haimkastner avatar Mar 08 '24 13:03 haimkastner