properties
Node.js project
Properties parser/stringifier
Version: 0.3.1
The module implements the Java properties specification and gives to you a powerful set of features that can be enabled or disabled at any time. Json files can be used to store complex data structures such as arrays or nested objects, but if you only want to save some properties, e.g. the database uri connection and credentials, valid json files can become a bit overloaded with metadata: curly braces, colons, commas and especially a lot of double quotes. Compare this two versions:
c = 1
[a]
a = 1
[b]
b = 1
{
"a": {
"a": "1"
},
"b": {
"b": "1"
},
"c": "1"
}
Which do you prefer? Which is more readable? Can you write comments in json files?
The stringified properties are parsed the right way reading character by character instead of reading lines, splitting them, using regular expressions and all the easy to implement but slow techniques.
There are several additional features you can use, some of them are: sections, variables, define your custom comment and key-value separator characters, replacers and revivers similar to the json callbacks, pretty print the stringified properties, convert special characters to their unicode string representation, write comments, parse/stringify INI files and much more.
The disk access is buffered to reduce the memory footprint. The default buffer size is 16KB, a quite large for a simple properties file. Most often you'll have small files (less than 1KB, maybe 2KB), so a single I/O call will be done, but it's better to support buffering, just in case. If you prefer, you can avoid the buffers and work with strings with the parse() and stringify() functions, so you decide how to do the I/O access -typically you'll use fs.readFile() and fs.writeFile()-
The properties are case sensitive.
Installation
npm install properties
Example
var properties = require ("properties");
properties.config ({
comment: "# ",
separator: " = ",
sections: true
});
var p = {
p1: "v1",
p2: null,
p3: {
$comment: "A property",
$value: "v3"
},
p4: {
$comment: "An empty property\nwith multi-line comment"
},
s1: {
p1: 1,
p2: 2
}
};
properties.store ("file", p, { header: "My header" }, function (error){
if (error) return console.log (error);
properties.load ("file", function (error, p){
if (error) return console.log (error);
console.log (p);
});
});
file:
# My header
p1 = v1
p2 =
# A property
p3 = v3
# An empty property
# with multi-line comment
p4 =
Features
Sections
To add a section just write:
[<name>]
Example:
[My Section]
Additional information:
- The keys next to a section header will belong to that section.
- Different sections can have keys with the same name.
- The keys added before the first section are considered global and don't belong to any section.
- It's not possible to nest sections inside other sections.
- Duplicate sections replaces the previous section with the same name, they are not merged.
Enable:
properties.config ({ sections: true });
Disable:
properties.config ({ sections: false });
Sections are disabled by default.
Variables
To get the value of a key:
a = 1
b = ${a}
The value of b
will be 1
.
Take into account that the keys can belong to sections. In the previous example, a
and b
are global properties. To reference a key within a section just prefix the section followed by a |
. Example:
a = 1
[Section1]
a = 2
[Section2]
a = 3
b = ${a}${Section1|a}${Section2|a}
The value of b
will be 123
.
You can also nest variables inside other variables, in other words, you can create variables dynamically. Example:
[Section1]
a = 12
[Section2]
123 = a
b = ${Section2|${Section1|a}3}
The value of b
will be a
.
You can use a variable anywhere. Look at the examples to see what you can do with variables.
Enable:
properties.config ({ variables: true });
Disable:
properties.config ({ variables: false });
Variables are disabled by default.
If you want to enable both the sections and the variables just write:
properties.config ({ variables: true, sections: true });
Customize tokens
You can also add new characters that can be used to write comments or to separate keys from values. For example, we want parse a text that uses ;
to write comments:
properties.config ({ allowedComments: [";"] });
The properties specification says that #
and !
can be used to write comments. These characters will always be allowed, so the allowedComments
property adds ;
to the valid set of tokens to write comments.
Similarly, you can add new characters to separate keys from values:
properties.config ({ allowedSeparators: ["-", ">"] });
allowedComments
and allowedSeparators
are used to parse the files. If you want to stringify an object and write comments with ;
and separators with -
you have to use comment
and separator
:
properties.config ({ comment: "; ", separator: " - " });
To reset to the default values:
properties.config ({
comment: null,
allowedComments: [],
separator: null,
allowedSeparators: []
});
INI files
Enabling the sections and adding ;
to the set of valid characters to write comments this module can also parse and stringify INI files:
properties.config ({
sections: true,
comment: "; ",
allowedComments: [";"]
});
Take into account that comment
and separator
can contain blank spaces (space, \t or \f) but allowedComments
and allowedSeparators
expect an array of characters.
Methods
properties.config([settings])
Enables and configures additional features.
The possible settings are:
- comment. String. The characters used to write comments. It's used when the object is stringified. Default is
#
. - separator. String. The characters used to separate keys from values. It's used when the object is stringified. Default is
=
. - allowedComments. Array. An array of characters that can be used to parse comments.
#
and !
are always considered comment tokens. - allowedSeparators. Array. An array of characters that can be used to parse key-value separators.
=
, :
and <blank space>
are always considered separator tokens. - sections. Boolean. Enables the sections usage. Default is false.
- variables. Boolean. Enables the variables usage. Default is false.
properties.load(file[, settings], callback)
Loads a properties file. The callback receives the error and the loaded properties. The loaded properties are just a JavaScript object in literal notation.
The possible settings are:
-
encoding. String. ascii
or utf8
. Default is utf8
.
-
bufferSize. Number. The buffer size used while reading the file. Default is 16KB.
-
reviver. Function. Callback executed for each property and section. Its funcionality is similar than the json reviver callback. The reviver receives two parameters, the key and the value. The returned value will be stored in the final object. If the function returns undefined the property is not added. If sections are enabled the reviver receives a third parameter, the section. When a section is found, the key and the value are set to null. The returnd value will be used to store the section, if it's undefined the section is not added.
For example, a reviver that does nothing:
properties.config ({ sections: true });
var reviver = function (key, value, section){
console.log (key, value, section);
if (key === null){
return section;
}
return value;
}
properties.load ("file", { reviver: reviver }, function (error, props){
console.log (props);
});
file:
a = 1
[section 1]
a = 1
[section 2]
a = 1
properties.parse(str[, settings])
Does the same as load() but does not perform any I/O access, the input is the given string. The only setting property is the reviver. Only throws exceptions if the variable substitution is enabled.
properties.store(file, obj[, settings], callback)
Stores a JavaScript object in literal notation -the properties- to a file. The callback receives a possible error.
All the non printable unicode characters (C0 and C1 control codes: 0-31 and 127-159) are converted to its unicode string representation, e.g. 0x00 (NUL) is converted to \u0000.
The properties can be null and can have comments. To write comments you must use an object with a $comment
and $value
properties. Some examples:
var props = {
a: "a value",
b: null,
c: {
comment: "c comment",
value: "c value"
},
d: {
comment: "d comment",
value: null
},
e: {
comment: "e comment"
},
f: {
value: "f value"
},
g: {
},
h: {
$comment: "h section",
$value: {
a: "a value",
b: {
$comment: "b comment",
$value: "b value"
}
}
}
};
The possible settings are:
- encoding. String.
ascii
or utf8
. If ascii
is used, all the characters with code greater than 127 are converted to its unicode string representation. Default is utf8
. - bufferSize. Number. The buffer size used while writing the file. Default is 16KB.
- header. String. A comment to write at the beginning of the file.
- pretty. Boolean. If true, the stringified properties are well formatted: tabbed and word wrapped at 80 columns.
- replacer. Function. The same as the reviver function but if the returned value is undefined the property or section is not stringified. Receives two parameters and optionally three if section are enabled.
The comments (from properties and header) can be written as a multi-line comment, for example, if you write a property:
a: {
comment: "line 1\nline2"
value: "b"
}
Then this will be written:
#line 1
#line 2
a=b
The line separator could also be \r\n
. Line separators are only used to split the comment, that is, if you're on Linux and write a comment line1\r\nline2
, a \n
will be used to write these lines.
Please, note that the ECMAScript specification does not guarantee the order of the object properties, so the module cannot guaranteee that the properties will be stored with the same order. This modules guarantees that if sections are enabled, the global properties will be written before the sections to avoid possible errors.
ECMA-262 does not specify enumeration order. The de facto standard is to match insertion order, which V8 also does, but with one exception:
V8 gives no guarantees on the enumeration order for array indices (i.e., a property name that can be parsed as a 32-bit unsigned integer).
properties.stringify(obj[, settings])
Does the same as store() but does not perform any I/O access, the output is a string. The possible setting properties are the pretty boolean and the reviver.