Quantity and Unit Definition Schema

August 2, 2026 · View on GitHub

Units.NET quantity and unit definitions are JSON files in Common/UnitDefinitions. Each file describes one quantity, the units that belong to it, their conversions, abbreviations, and optional code-generation behavior.

This is the contributor-facing schema reference for those files. The deserialization types in CodeGen/JsonTypes and the code generator are the implementation source of truth.

Important

The current JSON deserializer is permissive: unknown properties are ignored and some missing properties are not rejected until generated code is compiled or tested. Treat properties marked as required here as required, and do not rely on unknown properties being accepted.

For the contribution workflow and style conventions, see Adding a New Quantity or Unit.

Minimal definition

The following is a minimal definition of a linear quantity with one unit:

{
  "Name": "Length",
  "BaseUnit": "Meter",
  "XmlDocSummary": "Length is a measure of distance.",
  "BaseDimensions": {
    "L": 1
  },
  "Units": [
    {
      "SingularName": "Meter",
      "PluralName": "Meters",
      "BaseUnits": {
        "L": "Meter"
      },
      "FromUnitToBaseFunc": "{x}",
      "FromBaseToUnitFunc": "{x}",
      "Localization": [
        {
          "Culture": "en-US",
          "Abbreviations": [ "m" ]
        }
      ]
    }
  ]
}

By convention, the filename is <Name>.json, such as Length.json. Quantity and unit names use PascalCase and must be valid C# identifiers because they become generated type and member names.

UnitsNet.Modular compatibility

UnitsNet.Modular adds the optional Namespace field. It defaults to UnitsNet and becomes part of the stable semantic ID Namespace.Name; use a namespace you own for custom and third-party definitions. Repository CodeGen ignores this field.

Add a custom definition to a Modular project as a Roslyn AdditionalFiles item. See Add custom quantities for registration and selection.

Quantity object

The root JSON object represents a quantity.

PropertyTypeRequired/defaultDescription
NamestringRequiredPascalCase quantity name used for the generated quantity type and unit enum. Conventionally matches the filename.
NamespacestringUnitsNetUnitsNet.Modular only. Namespace for generated types and part of the stable semantic ID Namespace.Name. Ignored by repository CodeGen.
BaseUnitstringRequiredSingularName of the unit through which conversions are performed. It must identify exactly one entry in Units.
XmlDocSummarystringRequiredXML documentation summary for the generated quantity type. XML documentation elements such as <c> may be used.
XmlDocRemarksstringOptionalAdditional XML documentation remarks for the generated quantity type. Often contains a reference URL.
BaseDimensionsobjectAll exponents default to 0Exponents of the seven SI base dimensions. See Base dimensions.
AffineOffsetTypestringOptionalMarks an affine quantity and names the quantity used to represent differences, such as TemperatureDelta for Temperature.
Logarithmicboolean-likefalseGenerates logarithmic arithmetic and implements ILogarithmicQuantity. Existing definitions use the legacy string "True"; a JSON boolean is also accepted by the current deserializer.
LogarithmicScalingFactorinteger-like1Multiplier applied to the conventional factor of 10 for logarithmic arithmetic. Existing definitions use strings such as "1" and "2"; JSON integers are also accepted. Only meaningful when Logarithmic is true. See Logarithmic quantities.
ObsoleteTextstringOptionalGenerates an Obsolete attribute with this message for the quantity and its generated numeric extension methods.
Unitsarray of unit objectsRequiredUnits belonging to the quantity. At least one unit is required, and one must match BaseUnit.

AffineOffsetType and Logarithmic describe different arithmetic models and must not be combined.

Base dimensions

BaseDimensions maps SI dimension symbols to integer exponents. Missing dimensions have exponent zero.

KeyDimensionExample
LLengthLength: { "L": 1 }
MMassDensity: { "M": 1, "L": -3 }
TTimeFrequency: { "T": -1 }
IElectric currentElectricCurrent: { "I": 1 }
ΘThermodynamic temperatureTemperature: { "Θ": 1 }
NAmount of substanceAmountOfSubstance: { "N": 1 }
JLuminous intensityLuminousIntensity: { "J": 1 }

The temperature key is the Greek capital theta Θ, not the word Theta. Dimensionless quantities may omit BaseDimensions or use an empty object.

BaseDimensions describes the quantity's dimensional exponents. It is distinct from:

  • BaseUnit, the intermediate unit used for conversions.
  • A unit's BaseUnits, the concrete SI base-unit choices used for unit-system selection.

Unit object

Each entry in Units represents one unit.

PropertyTypeRequired/defaultDescription
SingularNamestringRequiredPascalCase singular unit name. Becomes an enum member and part of generated factory and conversion member names. Must be unique within the quantity.
PluralNamestringRequiredPascalCase plural unit name. Used in generated member names such as Length.Meters.
FromUnitToBaseFuncstringRequiredC# expression that converts {x} from this unit to the quantity's BaseUnit.
FromBaseToUnitFuncstringRequiredInverse C# expression that converts {x} from the quantity's BaseUnit to this unit.
BaseUnitsobjectOptionalConcrete SI base-unit names used to match this unit to a UnitSystem. See Base units.
Prefixesarray of stringsEmptyPrefix names for which CodeGen generates additional units. See Prefixes.
Localizationarray of localization objectsRequiredCulture-specific abbreviations. Every unit should define en-US.
XmlDocSummarystringOptionalXML documentation summary for the generated unit enum member.
XmlDocRemarksstringOptionalAdditional XML documentation remarks for the generated unit enum member. Often contains a reference URL.
ObsoleteTextstringOptionalGenerates an Obsolete attribute with this message for the unit and its generated numeric extension methods.

The base-unit entry should use identity conversion expressions:

"FromUnitToBaseFunc": "{x}",
"FromBaseToUnitFunc": "{x}"

Conversion expressions

Conversions always go through the quantity's BaseUnit:

source unit --FromUnitToBaseFunc--> BaseUnit --FromBaseToUnitFunc--> target unit

The conversion properties contain C# numeric expressions. CodeGen replaces every {x} placeholder with the input value and emits the expression into generated C# code. Expressions may use numeric literals, arithmetic operators, parentheses, and available APIs such as Math.PI.

The two expressions must be inverses. For example, with meters as the base unit:

{
  "SingularName": "Centimeter",
  "PluralName": "Centimeters",
  "FromUnitToBaseFunc": "{x} / 100",
  "FromBaseToUnitFunc": "{x} * 100"
}

Affine units include an offset:

{
  "SingularName": "DegreeCelsius",
  "PluralName": "DegreesCelsius",
  "FromUnitToBaseFunc": "{x} + 273.15",
  "FromBaseToUnitFunc": "{x} - 273.15"
}

Use {x} exactly, preserve exact constituent constants where possible, and follow the conversion function conventions. Since expressions are C# source rather than a language-neutral expression format, other platform generators must translate the supported expression syntax.

Base units

BaseUnits maps the same seven dimension keys to singular unit names from the corresponding SI base quantities. It allows APIs such as new Length(1, UnitSystem.SI) to select a suitable unit.

For example, the newton has:

"BaseUnits": {
  "L": "Meter",
  "M": "Kilogram",
  "T": "Second"
}

Only the concrete unit choice is stored; dimensional exponents come from the quantity's BaseDimensions. For example, an area unit can use { "L": "Centimeter" } even though the length dimension has exponent 2.

BaseUnits may be omitted when no meaningful mapping exists, such as for a gallon or a dimensionless ratio.

Prefixes

Prefixes tells CodeGen to derive additional units from the current unit. For example:

"Prefixes": [ "Milli", "Kilo", "Mega" ]

For each prefix, CodeGen:

  • Creates singular and plural names by prepending the prefix.
  • Adjusts both conversion expressions by the prefix factor.
  • Prefixes each localized abbreviation, unless an explicit override is configured.
  • Attempts to infer prefixed BaseUnits.

Accepted metric prefixes are:

Yocto, Zepto, Atto, Femto, Pico, Nano, Micro, Milli, Centi, Deci, Deca, Hecto, Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, and Yotta.

Accepted binary prefixes are:

Kibi, Mebi, Gibi, Tebi, Pebi, and Exbi.

Do not also define a generated prefixed unit explicitly.

Do not use Prefixes when the prefix would apply to only part of a powered or compound unit and change the meaning, or when it would create an abbreviation already used by another unit of the same quantity. Define explicit units instead. For example, define CubicMillimeterPerKilogram for mm³/kg instead of generating MillicubicMeterPerKilogram, and define ThousandCubicMeter with an abbreviation such as 10³·m³ when the intended unit is 1000 cubic meters rather than cubic kilometers.

CodeGen fails when Prefixes is used on a unit that looks like the powered unit itself, such as a unit name starting with Square or Cubic, or an abbreviation starting with , , ft³, or similar powered unit symbols. For example, generating Kilo from SquareMeter would produce km², which means one square kilometer (1e6 m²), not one thousand square meters. Generating it from CubicMeter would produce km³, which means one cubic kilometer (1e9 m³), not one thousand cubic meters.

This is a naming and abbreviation heuristic, not a dimensional-analysis check. Derived units such as Watt, Joule, and Ohm have powered SI base dimensions, but can safely use Prefixes because their own abbreviations are not powered unit symbols. They produce unambiguous units such as MW, MJ, and .

Localization object

Each Localization entry configures abbreviations for one culture.

PropertyTypeRequired/defaultDescription
CulturestringRequired.NET culture name, such as en-US, ru-RU, or zh-CN.
Abbreviationsarray of stringsEmptyUnit symbols and parsing aliases. The first abbreviation is the default used for formatting. Empty is valid for units such as Ratio.DecimalFraction.
AbbreviationsForPrefixesobjectOptionalExplicit abbreviations for selected generated prefixes. Each key is a configured prefix name and each value is a string or array of strings.

By default, CodeGen prepends the localized prefix symbol to every abbreviation. Use AbbreviationsForPrefixes when that would produce the wrong symbol or symbol order:

{
  "Culture": "en-US",
  "Abbreviations": [ "∆°C" ],
  "AbbreviationsForPrefixes": {
    "Milli": "∆m°C"
  }
}

Follow the abbreviation naming conventions. Abbreviation ambiguity across different quantities is allowed, but aliases must not make two units of the same quantity indistinguishable.

Specialized quantity models

Linear quantities

A quantity is linear when both AffineOffsetType and Logarithmic are omitted. Generated arithmetic operates directly on converted values. This is the default and applies to quantities such as Length, Mass, and Power.

Affine quantities

An affine quantity represents points on a scale where differences use a separate quantity type. Set AffineOffsetType to that difference quantity:

"AffineOffsetType": "TemperatureDelta"

Temperature.json is the current example. Its unit conversions include scale offsets, while TemperatureDelta.json defines conversions between differences without absolute-scale offsets.

Logarithmic quantities

Set Logarithmic on a quantity to generate logarithmic arithmetic:

"Logarithmic": "True",
"LogarithmicScalingFactor": "2"

The string representation above is retained by existing definitions for compatibility. The values deserialize to a Boolean and an integer respectively.

Let n be the JSON LogarithmicScalingFactor. CodeGen exposes and uses the effective scaling factor:

$\text{text} \text{S} = 10 \times \text{n} \text{logarithmic} \text{value} = \text{S} \times \text{log10}(\text{linear} \text{value}) \text{linear} \text{value} = 10^(\text{logarithmic} \text{value} / \text{S}) $

Therefore:

Quantity modelJSON valueEffective factorTypical relationship
Power or generic level11010 × log10(P/P₀)
Field amplitude, such as voltage22020 × log10(V/V₀)

The factor is used when generated operators and LogarithmicQuantityExtensions convert values to linear space for addition, subtraction, sums, and means. It is not automatically applied to FromUnitToBaseFunc or FromBaseToUnitFunc; those expressions still define conversion between the logarithmic units and their reference levels. For example, converting dBm to dBW subtracts 30.

Current examples are:

Logarithmic quantities require custom arithmetic test values; see the contribution workflow.

Quantity relations

Cross-quantity multiplication, division, and inverse relations are not properties of an individual quantity definition. They are declared separately in Common/UnitRelations.json.

Each entry has this form:

ResultQuantity.ResultUnit = LeftQuantity.LeftUnit * RightQuantity.RightUnit

For example:

"Force.Newton = Mass.Kilogram * Acceleration.MeterPerSecondSquared"

CodeGen infers commutative multiplication and corresponding division operators. Append -- NoInferredDivision when the inferred division would be ambiguous. Use 1 as the result for inverse relationships and double for a unitless numeric operand.

Internal and reserved properties

The deserialization model contains two unit properties that are not used by normal definitions:

  • SkipConversionGeneration defaults to false and suppresses generated convenience conversion members, numeric extensions, and related tests. No current unit definition uses it.
  • AllowAbbreviationLookup defaults to true, but the current generators do not consume it. Setting it has no observable effect.

Do not use these properties in contributor definitions without a corresponding CodeGen change and tests. Quantity.Relations is also internal generated state populated from Common/UnitRelations.json, not a property to set in a quantity definition.

Some existing definitions contain historical properties such as XmlDoc, XmlDocsRemarks, BaseType, or OmitExtensionMethod. The current deserialization model does not recognize them, so they have no effect and must not be copied into new definitions.

Validation workflow

After changing a definition:

  1. Run generate-code.bat or dotnet run --project CodeGen.
  2. Inspect the generated changes.
  3. Add or update independently sourced conversion test values.
  4. Run build.bat or dotnet build UnitsNet.slnx.
  5. Run the relevant tests.

Generated files under GeneratedCode must not be edited manually.