HOCON
C# implementation of Lightbend's HOCON (Human-Optimized Object Configuration Notation)
Install / Use
/learn @akkadotnet/HOCONREADME
HOCON (Human-Optimized Config Object Notation)
C# implementation of Typesafe's HOCON (Human-Optimized Object Configuration Notation)
Installation
To install HOCON via NuGet:
PS> Install-Package Hocon.Configuration
Nightly Build Access
If you need access to nightly HOCON builds, you can get them via the Akka.NET nightly build NuGet feed.
Spec
This is an informal spec, but hopefully it's clear.
Goals / Background
The primary goal is: keep the semantics (tree structure; set of types; encoding/escaping) from JSON (JavaScript Object Notation), but make it more convenient as a human-editable config file format.
The following features are desirable, to support human usage:
- less noisy / less pedantic syntax
- ability to refer to another part of the configuration (set a value to another value)
- import/include another configuration file into the current file
- a mapping to a flat properties list such as Java's system properties
- ability to get values from environment variables
- ability to write comments
Implementation-wise, the format should have these properties:
- a JSON superset, that is, all valid JSON should be valid and should result in the same in-memory data that a JSON parser would have produced.
- be deterministic; the format is flexible, but it is not heuristic. It should be clear what's invalid and invalid files should generate errors.
- require minimal look-ahead; should be able to tokenize the file by looking at only the next three characters. (right now, the only reason to look at three is to find "//" comments; otherwise you can parse looking at two.)
HOCON is significantly harder to specify and to parse than JSON. Think of it as moving the work from the person maintaining the config file to the computer program.
Default HOCON Configuration Sources
By default the HOCON library will look for HOCON configurations in the following locations whenever you call the Hocon.Configuration.ConfigurationFactory.Default() method:
- [.NET Core / .NET Framework] An "app.conf" or an "app.hocon" file in the current working directory of the executable when it loads;
- [.NET Framework] - the
<hocon>ConfigurationSectioninsideApp.configorWeb.config; or - [.NET Framework] - and a legacy option, to load the old
<akka>HOCON section for backwards compatibility purposes with all users who have been using HOCON with Akka.NET.
Definitions
-
a key is a string JSON would have to the left of
:and a value is anything JSON would have to the right of:. i.e. the two halves of an object field. -
a value is any "value" as defined in the JSON spec, plus unquoted strings and substitutions as defined in this spec.
-
a simple value is any value excluding an object or array value.
-
a field is a key, any separator such as ':', and a value.
-
references to a file ("the file being parsed") can be understood to mean any byte stream being parsed, not just literal files in a filesystem.
Syntax
Much of this is defined with reference to JSON; you can find the JSON spec at http://json.org/ of course.
Unchanged from JSON
- files must be valid UTF-8
- quoted strings are in the same format as JSON strings
- values have possible types: string, number, object, array, boolean, null
- allowed number formats matches JSON; as in JSON, some possible
floating-point values are not represented, such as
NaN
Comments
Anything between // or # and the next newline is considered a comment
and ignored, unless the // or # is inside a quoted string.
Omit root braces
JSON documents must have an array or object at the root. Empty files are invalid documents, as are files containing only a non-array non-object value such as a string.
In HOCON, if the file does not begin with a square bracket or
curly brace, it is parsed as if it were enclosed with {} curly
braces.
A HOCON file is invalid if it omits the opening { but still has
a closing }; the curly braces must be balanced.
Key-value separator
The = character can be used anywhere JSON allows :, i.e. to
separate keys from values.
If a key is followed by {, the : or = may be omitted. So
"foo" {} means "foo" : {}
Commas
Values in arrays, and fields in objects, need not have a comma
between them as long as they have at least one ASCII newline
(\n, decimal value 10) between them.
The last element in an array or last field in an object may be followed by a single comma. This extra comma is ignored.
[1,2,3,]and[1,2,3]are the same array.[1\n2\n3]and[1,2,3]are the same array.[1,2,3,,]is invalid because it has two trailing commas.[,1,2,3]is invalid because it has an initial comma.[1,,2,3]is invalid because it has two commas in a row.- these same comma rules apply to fields in objects.
Whitespace
The JSON spec simply says "whitespace"; in HOCON whitespace is defined as follows:
- any Unicode space separator (Zs category), line separator (Zl category), or paragraph separator (Zp category), including nonbreaking spaces (such as 0x00A0, 0x2007, and 0x202F). The BOM (0xFEFF) must also be treated as whitespace.
- tab (
\t0x0009), newline ('\n' 0x000A), vertical tab ('\v' 0x000B), form feed (\f' 0x000C), carriage return ('\r' 0x000D), file separator (0x001C), group separator (0x001D), record separator (0x001E), unit separator (0x001F).
In Java, the isWhitespace() method covers these characters with
the exception of nonbreaking spaces and the BOM.
While all Unicode separators should be treated as whitespace, in this spec "newline" refers only and specifically to ASCII newline 0x000A.
Duplicate keys and object merging
The JSON spec does not clarify how duplicate keys in the same object should be handled. In HOCON, duplicate keys that appear later override those that appear earlier, unless both values are objects. If both values are objects, then the objects are merged.
Note: this would make HOCON a non-superset of JSON if you assume that JSON requires duplicate keys to have a behavior. The assumption here is that duplicate keys are invalid JSON.
To merge objects:
- add fields present in only one of the two objects to the merged object.
- for non-object-valued fields present in both objects, the field found in the second object must be used.
- for object-valued fields present in both objects, the object values should be recursively merged according to these same rules.
Object merge can be prevented by setting the key to another value first. This is because merging is always done two values at a time; if you set a key to an object, a non-object, then an object, first the non-object falls back to the object (non-object always wins), and then the object falls back to the non-object (no merging, object is the new value). So the two objects never see each other.
These two are equivalent:
{
"foo" : { "a" : 42 },
"foo" : { "b" : 43 }
}
{
"foo" : { "a" : 42, "b" : 43 }
}
And these two are equivalent:
{
"foo" : { "a" : 42 },
"foo" : null,
"foo" : { "b" : 43 }
}
{
"foo" : { "b" : 43 }
}
The intermediate setting of "foo" to null prevents the object merge.
Unquoted strings
A sequence of characters outside of a quoted string is a string value if:
- it does not contain "forbidden characters": "$", '"', '{', '}', '[', ']', ':', '=', ',', '+', '#', '`', '^', '?', '!', '@', '*', '&', '' (backslash), or whitespace.
- it does not contain the two-character string "//" (which starts a comment)
- its initial characters do not parse as
true,false,null, or a number.
Unquoted strings are used literally, they do not support any kind of escaping. Quoted strings may always be used as an alternative when you need to write a character that is not permitted in an unquoted string.
truefoo parses as the boolean token true followed by the
unquoted string foo. However, footrue parses as the unquoted
string footrue. Similarly, 10.0bar is the number 10.0 then
the unquoted string bar but bar10.0 is the unquoted string
bar10.0. (In practice, this distinction doesn't matter much
because of value concatenation; see later section.)
In general, once an unquoted string begins, it continues until a forbidden character or the two-character string "//" is encountered. Embedded (non-initial) booleans, nulls, and numbers are not recognized as such, they are part of the string.
An unquoted string may not begin with the digits 0-9 or with a
hyphen (-, 0x002D) because those are valid characters to begin a
JSON number. The initial number character, plus any valid-in-JSON
number characters that follow it, must be parsed as a number
value. Again, these characters are not special inside an
unquoted string; they only trigger number parsing if they appear
initially.
Note that quoted JSON strings may not contain control characters (control characters include some whitespace characters, such as newline). This rule is from the JSON spec. However, unquoted strings have no restriction on control characters, other than the ones listed as "forbidden characters" above.
Some of the "forbidden characters" are forbidden because they already have meaning in JSON or HOCON, others are essentially reserved keywords to allow future extensions to this spec.
Multi-line strings
Multi-line strings are similar to Python or Scala, using triple
quotes. If the three-character sequence """ appears, then all
Unicode characters until a closing """ sequence are used
unmodified to create a string value. Newlines and whitespace
receive no special treatment. Unlike Scala, and unlike JSON quoted
strings, Unicode escapes are not interpreted in trip
Related Skills
node-connect
338.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
338.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.6kCommit, push, and open a PR
