Index: chainedconfigparser.py |
diff --git a/chainedconfigparser.py b/chainedconfigparser.py |
index ce26b04b50e175e1cbd99a772f2a589acd1ac64c..9691174ae5853a28b9f06402f2003b79447599a0 100644 |
--- a/chainedconfigparser.py |
+++ b/chainedconfigparser.py |
@@ -6,6 +6,7 @@ import os |
import io |
import ConfigParser |
from StringIO import StringIO |
+from collections import OrderedDict |
class Item(tuple): |
@@ -157,6 +158,79 @@ class ChainedConfigParser(ConfigParser.SafeConfigParser): |
raise ConfigParser.NoSectionError(section) |
raise ConfigParser.NoOptionError(option, section) |
+ def as_json_object(self, section): |
+ r"""Parse a given section into a JSON object. |
+ |
+ Parse arbitrary key/value pairs from 'section' of the current |
+ configuration into a nested JSON object. |
+ |
+ The following rules need to be considered: |
+ |
+ * An option's key may be declared as a series of nested dictionary keys, |
+ seperated by '.'. |
+ * An option's key must end with '[]', to mark this option as an array |
+ * When an option is marked as an array, no other nested objects may |
+ follow |
+ * An array is expandable by the ConfigParser's '+=' token. |
+ * Values may be marked as `number` or `bool` by prefixing them |
+ accordingly: |
+ * bool:<value> |
+ * number:<value> |
+ |
+ Example: |
+ |
+ { |
+ "good": false, |
+ "bar": { |
+ "baz": ["bar", "bazinga"] |
+ foo = foo }, |
+ bar.baz[] = bar "is": { |
+ baz.foo = a "integer": 1, |
+ baz.z[] = b bool:true "float": 1.4 |
+ bar.baz[] += bazinga ==> }, |
+ bad = bool:true "baz": { |
+ good = bool:false "foo": "a", |
+ is.integer = number:1 "z": ["b", true] |
+ is.float = number:1.4 }, |
+ "bad": true |
+ "foo": "foo" |
+ } |
+ """ |
+ def parse_values(v): |
+ if isinstance(v, list): |
+ return [parse_values(x) for x in v] |
+ |
+ if v.startswith('number:'): |
+ v = v.split(':', 1)[1] |
+ try: |
+ v = int(v) |
+ except ValueError: |
+ v = float(v) |
+ elif v == 'bool:true': |
+ v = True |
+ elif v == 'bool:false': |
+ v = False |
+ return v |
+ |
+ def setdefault_recursive(target, arr): |
+ if len(arr) == 2: |
+ target.setdefault(arr[0], parse_values(arr[1])) |
+ else: |
+ current = target.setdefault(arr[0], OrderedDict()) |
+ setdefault_recursive(current, arr[1:]) |
+ |
+ data = self.items(section) |
+ result = OrderedDict() |
+ |
+ for k, v in data: |
+ if k.endswith('[]'): |
+ k = k[:-2] |
+ v = v.split() |
+ |
+ setdefault_recursive(result, k.split('.') + [v]) |
+ |
+ return result |
+ |
def readfp(self, fp, filename=None): |
raise NotImplementedError |