Search code examples
javaunits-of-measurementjsr363jsr-275

Setting an alias name on a ProductUnit fails to return that alias during serialization


Task

I am trying to set an alias name on a UOM ProductUnitto allow the unit to be displayed in a human-friendly name in JSON/my frontend. The unit in question is "kg/h", so a mass-flow rate derived from the SI basic units "kg" and "s".

However, while setting the alias via the SimpleUnitFormat method

public abstract void alias(Unit<?> unit, String alias);

returns no error, the alias name is disregarded. I am not sure where I go wrong, as code comments on SimpleUnitFormat say "Attaches a system-wide alias to this unit".

Problem

I expect to be able to parse the string "kg/h" into a Indrya unit and if serializing that unit, get back "kg/h". What I instead get back is "(kg/s)*3600"

The code in form of a unit-test:

public class UnitsTest {
    private static final SimpleUnitFormat fs;
    private static final Unit<MassFlowRate> KILOGRAM_PER_HOUR;
    static {
        fs = SimpleUnitFormat.getInstance();
        // KILOGRAM_PER_SECOND is defined in si.uom.SI
        KILOGRAM_PER_HOUR = KILOGRAM_PER_SECOND.multiply(3600);
        fs.alias(KILOGRAM_PER_SECOND.multiply(3600), "kg/h");
    }


    @Test
    void testFlowRate() {
        String unitName = "kg/h";
        Unit<? extends Quantity<?>> rate = fs.parse(unitName);
        Assertions.assertEquals(unitName, rate.toString()); //<-- fails
        Assertions.assertEquals(rate, KILOGRAM_PER_HOUR);
    }
}

And the assertEquals() fails with the following:

org.opentest4j.AssertionFailedError: 
Expected :kg/h
Actual   :(kg/s)*3600

As you can see, the String-representation of the derived unit ("(kg/s)*3600") is physically correct, but not what my end-users would want to see.

What I tried

Replacing rate.toString() with rate.getName() doesn't work either, it just yields null. Replacing rate.toString() with rate.getSymbol() doesn't work either, it also yields null.

Another attempt with using the alternate() method instead of an alias like this:

KILOGRAM_PER_SECOND.multiply(3600).alternate("kg/h");

fails with

java.lang.IllegalArgumentException: The parent unit: (kg/s)*3600 is not an unscaled SI unit
    at tech.units.indriya.unit.AlternateUnit.<init>(AlternateUnit.java:91)
    at tech.units.indriya.AbstractUnit.alternate(AbstractUnit.java:336)

Based on this old answer, I tried switching to EBNFUnitFormat but that doesn't allow aliases.

I am sure I am misunderstanding the whole UoM/Indrya library here, but IDK how.

Environment

The problem comes up both on Windows and Linux. I am using Java 13 and here's the relevant parts of my POM:

<dependencies>
    <dependency>
        <groupId>tech.units</groupId>
        <artifactId>indriya</artifactId>
        <version>2.1.3</version>
    </dependency>

    <dependency>
        <groupId>si.uom</groupId>
        <artifactId>si-units</artifactId>
        <version>2.1</version>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.9.0</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.9.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Solution

  • In the end, it was easy:

    fs.label(KILOGRAM_PER_HOUR, "kg/h");
    

    This way, the unit gets serialized to kg/h in toString()