summaryrefslogtreecommitdiffhomepage
path: root/hocon.html.markdown
blob: b09e20f097c5605cb2e60818405019fd5ad7ba27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
---
language: hocon
filename: learnhocon.conf
contributors:
- [TehBrian, 'https://tehbrian.xyz']
---

Human-Optimized Configuration Object Notation, or HOCON, is a configuration and
data serialization format designed to be easily editable by humans.

It's a superset of JSON, meaning that any valid JSON is valid HOCON, but it
differs in being much less pedantic and opinionated. With its flexible yet
easily determinable syntax, resulting configuration files are often much less
noisy than some other formats.

Additionally, its support for comments makes it much better suited for
user-facing configurations than JSON.

```
// Comments can either look like this,
# or they can look like this.
// Anything after // or # is a comment.

##################
### THE BASICS ###
##################

# Everything in HOCON is either a key, a value, or a separator.
# : and = are separators. They separate the key from the value.
key: value
another_key = another_value

# You can use either separator with or without whitespace on either side.
colon1:value
colon2: value
colon3 : value
equals1=value
equals2= value
equals3 = value
# As you'll see, HOCON is very nonrestrictive regarding its syntax style.

# HOCON also isn't opinionated on how keys look.
THIS_IS_A_VALID_KEY: value
this-is-also-a-valid-key: value
keys can have spaces too: value
or even numbers like 12345: value
"you can even quote keys if you'd like!": value

# A key, followed by any separator, and then finally a value, is called a field.
this_entire_line_is: a field

###################
### VALUE TYPES ###
###################

# The types that a value can be are string, number, object, array, boolean, and
# null. Every value type except for array and object are called simple values.

## SIMPLE VALUES ##

quoted_string: "I like quoting my strings."
unquoted_string: I don't like quoting my strings.
# Special characters that cannot be used in unquoted strings are:
# $ " { } [ ] : = , + # ` ^ ? ! @ * &
# Unquoted strings do not support any kind of escaping. If using one of those
# special characters is desired, use a quoted string.
multi-line_string: """
  This entire thing is a string!
  One giant, multi-line string.
  You can put 'single' and "double" quotes without it being invalid.
"""

number: 123
negative: -123
fraction: 3.1415926536
scientific_notation: 1.2e6 // same as 1.2 * (10^6)

boolean: true # or false
empty: null

## ARRAYS ##

# Arrays hold lists of values.
# Values in arrays can be separated with commas..
array: [ 1, 2, 3, 4, 5 ]
fibonacci: [1,1,2,3,5,8,13]
multiples_of_5: [5, 10, 15, 20,] # Notice the trailing comma. That's okay here.
# or newlines..
friends: [
  "Brian"
  "Sophie"
  "Maya"
  "Sabina"
]
# or both!
ingredients: [
  "Egg",
  "Sugar",
  "Oil",
  "Flour", # Notice the trailing comma. That's okay here too.
]
# Once again, HOCON has a very loose syntax. Use whichever style you prefer.
no newline before or after bracket: ["This"
  "is"
  "an"
  "array!"]

# Just like any other value, arrays can hold other arrays.
array in array: [ [1, 2, 3], ["a", "b", "c"] ]
array in array in array: [ [ [1, 2], [8, 9] ], [ ["a", "b" ], ["y", "z"] ] ]

## OBJECTS ##

# Objects hold fields.
# Just like arrays, fields in objects can be separated with commas..
object: { key: value, another_key: another_value }
server_connection: {ip: "127.0.0.1", port: 80}
first: {letter: a, number: 1,} # Notice the trailing comma.
# or newlines..
power_grid: {
  max_capacity: 15000
  current_power: 1200
}
# or both!
food_colors: {
  carrot: orange,
  pear: green,
  apple: red,
  plum: purple,
  banana: yellow, # Trailing commas are okay here too!
}

# Arrays can hold objects just like any other value type.
coworkers: [
  {
    name: Jeff
    age: 27
  },
  {
    name: Henry
    age: 35
  },
  {
    name: Timmy
    age: 12
  }
]

# The field separator may be omitted if the key is followed by {
no_separator {
  key: value
  speed_of_light: very fast
  ten: 10

  # Objects can go inside other objects just like any other value.
  another_object {
    twenty: 20
    speed_of_sound: also pretty fast
  }
}

# In fact, the entirety of any HOCON document is an actually just an object.
# That object is called the root object. The only difference between it and any
# other object is that the curly brackets at the top and bottom of the document
# may be omitted.

# This means that HOCON documents can be formatted in the same way that
# regular objects can be formatted, including separating fields with commas
# rather than with newlines.

# Additionally, while the entirety of a HOCON document can be and is usually an
# object, it can also be an array. If it is an array, the opening and closing
# brackets at the top and bottom of the document must be explicitly written.

######################
### DUPLICATE KEYS ###
######################

is_happy: false
# If there is a duplicate key, the new value overrides the previous value.
is_happy: true
online_users: [Jacob, Mike]
# Same with arrays.
online_users: [Jacob, Mike, Henry]

# For objects, it's a bit different.
my_car: {
  color: blue
  speed: 9001
  passengers: null

  engine: {
    running: true
    temperature: 137
  }
}
# If there is a duplicate key and both values are objects,
# then the objects are merged.
my_car: {
  // These fields are added to the old, previous object.
  nickname: "My Favorite Car"
  type: 2-door sedan

  // Since the value of this duplicate key is NOT an object,
  // it simply overrides the previous value.
  speed: 60
  // Same with arrays. They override, not merge.
  passengers: ["Nate", "Ty"]

  // This object is recursively merged with the other object.
  engine: {
    // These two fields are added to the previous object.
    type: gas
    oil_level: 10
    // This field overrides the previous value.
    temperature: 179
  }
}

# Object merging is done two at a time. That is to say, the first two objects
# merge into one, then that object merges with the next object, and so on.

# Because of this, if you set a field with an object value to a non-object value
# and then back to an object value, the new object will completely override any
# previous value.

// Null, a non-object value, completely overrides the object value.
my_car: null

// Then, this object completely overrides null.
my_car: {
  nickname: "My New Car"
  type: 4-door minivan
  color: gray
  speed: 90
  passengers: ["Ayden", "Liz"]
}

###########################
### VALUE CONCATENATION ###
###########################

## SIMPLE VALUE CONCATENATION ##

# Simple values (all value types except objects and arrays) separated by
# whitespace are concatenated into a single string. The whitespace between
# values is preserved.
number_concatenation: 1 2 3 12.5 -3 2e5 // same as: "1 2 3 12.5 -3 2e5"
boolean_concat: true false true // "true false true"
null_concat: null null null // "null null null"
mixed_concat: 1 true null // "1 true null"

# String value concatenation can appear anywhere that a quoted string can.
number_concat_in_array: [1 2, 3 4, 5 6] // same as: ["1 2", "3 4", "5 6"]

# In fact, unquoted strings are actually just string value concatenations.
unquoted_string_concat: his name is jeff // same as: "his name is jeff"

# Going further, even keys that are unquoted strings are actually just string
# value concatenations.
this is a key: value // the KEY is the same as: "this is a key"
# The following field is identical to the field above.
"this is a key": value

# Quoted strings can also be concatenated. This will be useful later,
# when we cover substitutions.
quoted_string_concat: "her"" name" "is ""jenna" // same as: "her name is jenna"
# Notice that the whitespace (or lack thereof) between values is preserved.

## ARRAY CONCATENATION ##

# Arrays separated by whitespace are merged into a single array.
array_concat: [1, 2, 3] [4, 5, 6] // same as: [1, 2, 3, 4, 5, 6]

# Arrays cannot be concatenated with a non-array value.
//array_concat: true [false] results in an error
//array_concat: 1 [2] results in an error

## OBJECT CONCATENATION ##

# Objects separated by whitespace are merged into a single object.
# The merge functionality is identical to that of duplicate key object merging.
lamp: {on: true} {color: tan} // same as: {on: true, color: tan}

# Similarly to arrays, objects cannot be concatenated with a non-object value.
//object_concat: true {on: false} results in an error
//object_concat: 1 {number: 2} results in an error

########################
### PATH EXPRESSIONS ###
########################

# Path expressions are used to write out a path through the object graph.
# Think of it as navigating through objects to a specific field.
# Each object to traverse through is called an element, and each element is
# separated with a period.

country: {
  city: {
    neighborhood: {
      house: {
        name: "My House"
        address: 123 Example Dr.
      }
    }
  }
}
# For example, the path to the address of the house could be written as:
# country.city.neighborhood.house.address
# Country, city, neighborhood, house, and address are all elements.

# Path expressions are used in two places: substitutions (which will be
# covered in a moment), and as keys.
# That's right: keys themselves can also be path expressions.
foo: {
  bar: {
    baz: {
      number: 12
    }
  }
}
# Rather than tediously specifying each object, a path expression can be used.
# The following field represents the same object found above.
foo.bar.baz.number: 12

# Fields and objects specified with path expressions are merged in the same way
# that any object is usually merged.
foo.bar.baz.bool: true
// the object foo's value is: foo { bar { baz { number: 12, bool: true } } }

#####################
### SUBSTITUTIONS ###
#####################

# Substitutions refer to a specific value from some path expression.
# They're only allowed in values, not keys or nested inside other substitutions.

me: {
  favorite_animal: parrots
  favorite_food: cookies
}
# The syntax for a substitution is either ${path_expression} or
# ${?path_expression}. The latter syntax will be discussed in a moment.
my_fav_animal: ${me.favorite_animal}
my_fav_food: ${me.favorite_food}

# Substitutions are not parsed inside quoted strings. To get around this,
# either use an unquoted string or value concatenation.
animal_announcement: My favorite animal is ${my_fav_animal}
// the value is: My favorite animal is parrots
food_announcement: "My favorite food is "${my_fav_food}"!"
// the value is: "My favorite food is cookies!"

# Substitutions are parsed last in the document. Because of this, you can
# reference a key that hasn't been defined yet.
color_announcement: "My favorite color is" ${my_fav_color}"!"
// the value is: "My favorite color is blue!"
my_fav_color: blue

# Another effect of substitutions being parsed last is that substitutions will
# always use the latest, as in last, value assigned in the entire document,
# which includes merged objects.
color: green
their_favorite_color: ${color} // the value is: orange
color: orange

random_object: {
  number: 12
}
the_number: ${random_object.number} // the value is: 15
random_object: {
  number: 15
}

###############################
### UNDEFINED SUBSTITUTIONS ###
###############################

# A substitution using the ${path_expression} syntax with an undefined path
# expression, meaning a path expression that does not point to a defined value,
# is invalid and will therefore generate an error.
//${does.not.exist} will throw an error

# However, an undefined substitution using the ${?path_expression} syntax
# has different behavior depending on what it is the value of.
request: {
  # If it is the value of a field, then the field will not be created.
  response: ${?does.not.exist} // this field won't be created and does not exist
  type: HTTP
}

request: {
  # Additionally, if it would have overridden a previous value, then the
  # previous value remains unchanged.
  type: ${?does.not.exist} // request.type is still HTTP
}

# If it is a value in an array, then it is simply not added.
values: [ 172, "Brian", ${?does.not.exist}, null, true, ]
// the value is: [ 172, "Brian", null, true ]

# If it is part of simple value concatenation, it acts as an empty string.
final_string: "String One"${?does.not.exist}"String Two"
// the value is: "String OneString Two"

# If it is part of array concatenation, it acts as an empty array.
final_array: [ 1, 2, 3 ] ${?does.not.exist} [ 7, 8, 9 ]
// the value is: [ 1, 2, 3, 7, 8, 9 ]

# If it is part of object concatenation, it acts as an empty object.
final_array: { a: 1 } ${?does.not.exist} { c: 3 }
// the value is: { a: 1, c: 3 }

######################################
### SELF-REFERENTIAL SUBSTITUTIONS ###
######################################

# Substitutions normally "look forward" and use the final value defined in the
# document. However, in cases when this would create a cycle, the substitution
# looks only backwards.

# A field which contains a substitution that points to itself or points to
# other fields that eventually point back to itself is called a
# self-referential field.
letters: "a b c" // the value is: "a b c"
letters: ${letters}" d" // "a b c d"
letters: ${letters}" e" // "a b c d e"

PATH: [/bin] // the value is: [/bin]
PATH: ${PATH} [/usr/bin] // [/bin, /usr/bin]
PATH: ${PATH} [/usr/local/bin] // [/bin, /usr/bin, /usr/local/bin]

x: "x" // the value is: "x"
y: ${x}"y" // "xy"
x: ${y}"z" // "xyz"

##########################
### += FIELD SEPARATOR ###
##########################

# In addition to : and =, there actually exists another separator: +=
# A field separated with += acts as a self-referential array concatenation.
# In short, it appends an element to a previously defined array.

a: [1]
b: [1]
# This field:
a += 2 // the value is: [1, 2]
# functions the same as:
b: ${?b} [2] // the value is: [1, 2]

USERS: [/usr/luke] // the value is: [/usr/luke]
USERS += /usr/devon // [/usr/luke, /usr/devon]
USERS += /usr/michael // [/usr/luke, /usr/devon, /usr/michael]

# Since += only appends elements to a previously existing array, if the previous
# value was not an array, an error will be generated.
OTHER_USERS: /usr/luke
//OTHER_USERS += /usr/devon results in an error

# Notice that the underlying substitution syntax used is ${?path}, not ${path}.
# Recall that, using the ${?} syntax, an undefined substitution in array
# concatenation acts as an empty array. Because of this, it is perfectly
# acceptable if the field that is being set is initially undefined.
//z: [] not necessary
z += 3 // the value is: [3]
z += 4 // the value is: [3, 4]

NEW_USERS += /usr/sandra // the value is: [/usr/sandra]
NEW_USERS += /usr/kennedy // [/usr/sandra, /usr/kennedy]
NEW_USERS += /usr/robin // [/usr/sandra, /usr/kennedy, /usr/robin]

################
### INCLUDES ###
################

# Includes allow you to "import" one HOCON document into another.

# An include statement consists of the unquoted string "include" followed by
# whitespace and then a resource name, which is one of the following:
# - a single quoted string which is heuristically interpreted as a URL,
#   filename, or a Java classpath resource.
# - url(), file(), or classpath(), with the parentheses surrounding a quoted
#   string which is either a URL, filename, or classpath resource respectively.
# - required(), with the parentheses surrounding one of the above.
include "https://example.com/config.conf"
include "/foo/bar/config.conf"
include "config.conf"

include url("https://example.com/config.conf")
include file("/foo/bar/config.conf")
include classpath("config.conf")

# If the included file does not exist, it will be silently ignored and act as if
# it were an empty object. However, if it is wrapped around required(), then
# parsing will explicitly error if the file cannot be resolved.
//include required("doesnt_exist.conf") will error
//include required(url("https://example.com/doesnt_exist.conf")) will error
//include required(file("doesnt_exist.conf")) will error
//include required(classpath("doesnt_exist.conf")) will error

# The file specified by the include statement is called the included file, and
# the file which contains the include statement is called the including file.

# Including a file functions as if you directly replaced the include statement,
# wherever it may be, with the contents of the included file's root object.

# An included file must have an object as its root value and not an array.
# If the included file has an array as its root value, then it is invalid and
# an error will be generated.

# Pretend that the following is in a file called user_config.conf:
username: RandomUser1337
auto_login: true
color_theme: dark
screensaver: {
  image: usr/images/screensaver.jpg
  turn_on_after: 1m
}

# And then we include that file.
include file("user_config.conf")

# We can now reference values from that file!
path_to_user_screensaver: ${screensaver.image} //
greeting: "Welcome, "${username}"!" // the value is: "Welcome, RandomUser1337!"

# Duplicate keys override as they normally do.
status: "Auto Login: "${auto_login} // the value is: "Auto Login: true"
auto_login: false
status: "Auto Login: "${auto_login} // the value is: "Auto Login: false"

# Object merging is also the same as usual.
screensaver: {
  // This gets added to the screensaver object.
  enable_during_day: false
  // This overrides the previous value.
  turn_on_after: 30s
}

# Include statements can appear in place of a field. Anywhere that a field
# could appear, an include statement could appear as well.

# Pretend that the following is in a file called server_settings.conf:
max_connections: 10
url: example.com
port: 80
admin_page: {
  username: admin
  password: pass12345
}

# And then we include that file nested inside another object.
websites: {
  my_epic_website: {
    include file("server_settings.conf")
  }
}

# Now, we can reference the contents of server_settings.conf as if they
# had been written directly into the object my_epic_website.
server_port: ${websites.my_epic_website.port}

the_password: "The password is: "${websites.my_epic_website.admin_page.password}
// the value is: The password is: pass12345

max_conn: "Max Connections: "${websites.my_epic_website.max_connections}
// the value is: Max Connections: 10
```

### More Resources

+ [Official HOCON Specification](https://github.com/lightbend/config/blob/master/HOCON.md)
+ [HOCON Playground](https://hocon-playground.herokuapp.com)