- Parsing and Formatting
Parsing and Formatting
This page explains how developers can use FormatterSpec and ParserSpec to format quantity values and parse user input strings. It also covers integration with iTwin tools and components.
FormatterSpec and ParserSpec
FormatterSpec
FormatterSpec is the runtime object used to format numeric quantity values into display strings. It contains:
- The Format specification defining display rules
- Cached UnitConversionSpec objects for all display units
- The persistence unit (source unit for the value)
Creating a FormatterSpec:
Using a FormatterSpec:
ParserSpec
ParserSpec is the runtime object used to parse formatted strings back into numeric values. It contains:
- The Format specification for recognizing unit labels
- Cached UnitConversionSpec objects for all units in the phenomenon
- The persistence unit (target unit for parsed values)
Creating a ParserSpec:
Using a ParserSpec:
Parser Behavior
The Parser converts text strings into numeric quantity values by tokenizing the input and matching unit labels to known units. Understanding parser behavior helps you handle edge cases and errors correctly.
Parsing Process
Tokenization: The input string is broken down into tokens representing numbers, unit labels, and mathematical operators (if enabled).
Unit Label Matching: For each unit label token found, the parser attempts to match it against:
- Units explicitly defined in the format specification
- Units from the same phenomenon (unit family) provided by the UnitsProvider
- Alternate labels defined through the AlternateUnitLabelsProvider
Error Handling: The parser's behavior when encountering unrecognized unit labels depends on the format configuration:
- Unitless Format (no units defined in format definition): If a unit label is provided but cannot be matched to any known unit, the parser returns
ParseError.UnitLabelSuppliedButNotMatched. This prevents silent failures where typos like "12 im" (instead of "12 in") would incorrectly parse as "12 meters" when the persistence unit is meters. - Format with Units (units explicitly defined): If an unrecognized unit label is provided (e.g., "12 ABCDEF"), the parser falls back to the format's default unit for backward compatibility. For example, with a feet format, "12 ABCDEF" would parse as "12 feet".
- Unitless Format (no units defined in format definition): If a unit label is provided but cannot be matched to any known unit, the parser returns
Default Unit Behavior: If no unit label is provided in the input (e.g., just "12"), the parser uses the default unit specified in the format. For unitless formats, if the input contains multiple unit labels, the first successfully matched unit becomes the default for subsequent unitless values in the same expression.
Unit Conversion: Once units are matched, the parser applies the appropriate unit conversions to produce a value in the persistence unit specified by the
ParserSpec.
This error handling ensures that parsing errors are caught in unitless format contexts, preventing data corruption from unrecognized or mistyped unit labels, while maintaining backward compatibility for formats with explicitly defined units.
Example: Parsing Values
Basic parsing example
Mathematical Operations
The quantity formatter supports parsing mathematical operations, allowing users to enter expressions like "5 ft + 12 in - 6 in". The parser evaluates the expression and formats each value according to the specified format.
Enabling Mathematical Operations
Mathematical operations are disabled by default. To enable them, set the allowMathematicOperations property in your format:
Enabling mathematical operations
Example: Parsing Mathematical Expressions
Parsing mathematical operations
Limitations
Only plus (+) and minus (-) operators are currently supported. Other operators will return a parsing error or invalid input result.
Whitespace Handling
If a format uses a spacer that conflicts with the operators above, additional restrictions apply:
- Mathematical operations only apply when the operator is in front of whitespace. For example:
-2FT 6IN + 6INis equal to-2FT-6IN + 6IN-2FT-6IN - 6INis NOT equal to-2FT-6IN- 6IN
Whitespace limitation example
Composite Unit Handling
- For a value like
2FT 6IN-0.5, the-sign will be treated as a spacer and not subtraction. However, the0.5value will use the default unit conversion provided to the parser, because it's not a part of the composite unit when that composite is made up of only 2 units -FTandIN.
Composite unit limitation example
Usage in iTwin Tools and Components
This section explains how iTwin tools and components integrate FormatterSpec and ParserSpec for consistent quantity formatting and parsing.
QuantityFormatter Integration
The QuantityFormatter class in @itwin/core-frontend provides a convenient interface for formatting and parsing quantities. It manages:
- Default formats for each QuantityType, but there's an effort to move away from using
QuantityType. See Migrating from QuantityType to KindOfQuantity - Format overrides through UnitFormattingSettingsProvider
- Unit system management (metric, imperial, usCustomary, usSurvey)
- UnitsProvider registration
The QuantityFormatter is automatically initialized when IModelApp starts, creating cached FormatterSpec and ParserSpec objects for each QuantityType.
Measure Tools Examples
iTwin.js includes several measure tools that use QuantityFormatter to display formatted values and parse user input. Below are two representative examples showing the general pattern.
Example 1: MeasureDistanceTool - Formatting
The MeasureDistanceTool formats distance values for display:
MeasureDistanceTool formatting example
Typical format behavior:
Imperial: Displays as
X'-X"with inches to nearest 1/8"{ "composite": { "units": [{ "label": "'", "name": "Units.FT" }, { "label": "\"", "name": "Units.IN" }] }, "precision": 8, "type": "Fractional" }Metric: Displays as
Xmwith 4 decimal places{ "composite": { "units": [{ "label": "m", "name": "Units.M" }] }, "precision": 4, "type": "Decimal" }
Example 2: MeasureLocationTool - Parsing
The MeasureLocationTool parses user-entered angle strings:
MeasureLocationTool parsing example
Typical parsing behavior:
The parser accepts various angle formats:
24^34.5'- Using alternate label "^" for degrees24°34.5'- Using standard degree symbol45.5°- Decimal degrees45°30'15"- Degrees, minutes, seconds
The ParserSpec for angles includes conversions from all angular units (degrees, minutes, seconds, gradians) to the persistence unit (radians).
General Pattern for Tools and Components
When developing tools or components that format/parse quantities:
Identify the KindOfQuantity: Determine which KindOfQuantity your tool should use (e.g.,
DefaultToolsUnits.LENGTH,CivilUnits.STATION)Get FormatProps: Retrieve the format from the active FormatsProvider. If not found, provide a fallback format definition.
await IModelApp.quantityFormatter.setActiveUnitSystem("metric"); // When the default formats provider is used, ensure the desired unit system is active let formatProps = await IModelApp.formatsProvider.getFormat("DefaultToolsUnits.LENGTH"); if (!formatProps) { // Fallback: Define a hardcoded format for your tool formatProps = { composite: { units: [{ label: "m", name: "Units.M" }] }, precision: 1, type: "Decimal" }; }Convert to Format and Get Persistence Unit: Create a Format object from FormatProps and retrieve the persistence unit. Access the UnitsProvider from IModelApp.
const unitsProvider = IModelApp.quantityFormatter.unitsProvider; const format = new Format("length"); await format.fromJSON(unitsProvider, formatProps); const persistenceUnit = await unitsProvider.findUnitByName("Units.M");Create Specs: Create FormatterSpec and ParserSpec as needed
const formatterSpec = await FormatterSpec.create("length", format, unitsProvider, persistenceUnit); const parserSpec = await ParserSpec.create(format, unitsProvider, persistenceUnit);Format/Parse: Use the specs throughout your tool's lifecycle
const value = 5.5; const userInput = "10.5 m"; const formatted = formatterSpec.applyFormatting(value); // "5.5000 m" const parsed = parserSpec.parseToQuantityValue(userInput); // 10.5
Migrating from QuantityType to KindOfQuantity
Starting in iTwin.js 5.0, we encourage developers to move away from using QuantityType, and to instead use KindOfQuantity EC full names.
Why Migrate?
- Broader range of formatting capabilities: Not limited to nine built-in types
- Dynamic format definition: Work with formats defined in custom schemas
- Scalability: Use MutableFormatsProvider to add or override formats
- Schema integration: Better alignment with BIS schemas and domain models
QuantityType Replacement Table
| QuantityType | Actual KindOfQuantity (EC Full Name) |
|---|---|
| Length | DefaultToolsUnits.LENGTH |
| Angle | DefaultToolsUnits.ANGLE |
| Area | DefaultToolsUnits.AREA |
| Volume | DefaultToolsUnits.VOLUME |
| LatLong | DefaultToolsUnits.ANGLE |
| Coordinate | DefaultToolsUnits.LENGTH_COORDINATE |
| Stationing | CivilUnits.STATION |
| LengthSurvey | CivilUnits.LENGTH |
| LengthEngineering | AecUnits.LENGTH |
Schema Layers:
- DefaultToolsUnits - Common layer schema present in many iModels
- CivilUnits - Discipline-Physical layer for civil infrastructure
- AecUnits - Common layer for AEC applications
More information on schemas and their different layers can be found in BIS Organization.
Handling Missing Schemas
iModels might not include CivilUnits, DefaultToolsUnits, or AecUnits schemas. In such cases:
- Integrate your tools/components to use a
FormatsProvider - Add the missing KindOfQuantity and associated FormatProps through that FormatsProvider
- This works independently from schemas in iModels
Default Support
To support migration, IModelApp uses an internal QuantityTypeFormatsProvider by default, which provides formatProps for each KindOfQuantity in the table above. We still strongly encourage developers to either implement their own FormatsProvider or use SchemaFormatsProvider if possible.
Note: We plan to deprecate
QuantityTypeduring the iTwin.js 5.x lifecycle.
See Also
- Formats - Format specification reference
- Providers - Setting up UnitsProvider and FormatsProvider
- Unit Conversion - How unit conversions work
Last Updated: 23 January, 2026