This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/685-properties-json-serialization
in repository https://gitbox.apache.org/repos/asf/celix.git

commit aa29987def9709f35b569280d61a2dd0aead5d12
Author: Pepijn Noltes <pnol...@apache.org>
AuthorDate: Mon Apr 15 23:17:21 2024 +0200

    gh-685: Add properties encoding documentation
---
 documents/README.md              |   1 +
 documents/properties_encoding.md | 333 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 334 insertions(+)

diff --git a/documents/README.md b/documents/README.md
index 3930498a..c174acbe 100644
--- a/documents/README.md
+++ b/documents/README.md
@@ -86,6 +86,7 @@ bundles contains binaries depending on the stdlibc++ library.
   * [Apache Celix C Patterns](c_patterns.md)
 * Utils
   * [Apache Celix Properties & Filter](properties_and_filter.md)
+  * [Apache Celix Properties Encoding](properties_encoding.md)
 * Framework 
   * [Apache Celix Bundles](bundles.md)
   * [Apache Celix Services](services.md)
diff --git a/documents/properties_encoding.md b/documents/properties_encoding.md
new file mode 100644
index 00000000..c4cdb38d
--- /dev/null
+++ b/documents/properties_encoding.md
@@ -0,0 +1,333 @@
+---
+title: Apache Celix Properties Encoding
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+   
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Apache Celix Properties JSON Encoding
+
+## Introduction
+
+In Apache Celix, properties represent key-value pairs, often used for 
configuration. While these properties are not JSON
+objects inherently, they can be encoded to and decoded from JSON for 
interoperability or storage. This page explains how
+Apache Celix properties are encoded to and decoded from JSON.
+
+### Encoding limitations
+
+Except for empty arrays and the double values NaN, Infinity, and -Infinity, 
all Apache Celix properties types can
+be encoded to JSON.
+
+The reason for the empty array limitation is that for a properties array entry 
the array list element type is must be
+known, this is not possible to infer from an empty JSON array. To ensure that 
everything this is encoded, can be decoded
+again, a properties array entry with an empty array is not encoded to JSON.
+
+The reason for the double values NaN, Infinity, and -Infinity limitation is 
that JSON does not support these values.
+
+### Decoding limitations
+
+When decoding JSON to Apache Celix properties, the following limitations apply:
+
+- Mixed array types are not supported. For example, an array with both strings 
and longs cannot be decoded to a
+  properties' entry.
+- null values are not supported, because properties does not support a null 
value type.
+- Empty arrays are not supported, because the array list element type must be 
known, this is not possible to infer from
+  an empty JSON array.
+- JSON keys that collide on the created properties' key level are not 
supported.
+  See [Properties Decoding](##Properties Decoding) for more information.
+
+## Properties Encoding
+
+Apache Celix properties can be encoded to JSON using the 
`celix_properties_save`, `celix_properties_saveToStream`
+and `celix_properties_saveToString` functions. These functions take a 
properties object and encode it to a JSON object
+string. The encoding can be controlled using flags and can be done in a flat 
or nested structure.
+
+### Properties Flat Encoding
+
+By default, the encoding is done in a flat structure, because a flat structure 
ensures that all keys of the properties
+can be represented in JSON format. When properties are encoded to JSON in a 
flat structure, the reverse operation,
+decoding JSON that has been encoded from properties, will result in the same 
properties (except for the previously
+mentioned limitations (empty arrays and the double values NaN, Infinity, and 
-Infinity)).
+
+Flat Encoding example:
+
+```C
+#include <stdio.h>
+#include <celix/properties.h>
+
+int main() {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+    celix_properties_set(props, "single/strKey", "strValue");
+    celix_properties_setLong(props, "single/longKey", 42);
+    celix_properties_setDouble(props, "single/doubleKey", 2.0);
+    celix_properties_setBool(props, "single/boolKey", true);
+    celix_properties_assignVersion(props, "single/versionKey", 
celix_version_create(1, 2, 3, "qualifier"));
+
+    celix_array_list_t* strArr = celix_arrayList_createStringArray();
+    celix_arrayList_addString(strArr, "value1");
+    celix_arrayList_addString(strArr, "value2");
+    celix_properties_assignArrayList(props, "array/stringArr", strArr);
+
+    celix_array_list_t* longArr = celix_arrayList_createLongArray();
+    celix_arrayList_addLong(longArr, 1);
+    celix_arrayList_addLong(longArr, 2);
+    celix_properties_assignArrayList(props, "array/longArr", longArr);
+
+    celix_array_list_t* doubleArr = celix_arrayList_createDoubleArray();
+    celix_arrayList_addDouble(doubleArr, 1.0);
+    celix_arrayList_addDouble(doubleArr, 2.0);
+    celix_properties_assignArrayList(props, "array/doubleArr", doubleArr);
+
+    celix_array_list_t* boolArr = celix_arrayList_createBoolArray();
+    celix_arrayList_addBool(boolArr, true);
+    celix_arrayList_addBool(boolArr, false);
+    celix_properties_assignArrayList(props, "array/boolArr", boolArr);
+
+    celix_array_list_t* versionArr = celix_arrayList_createVersionArray();
+    celix_arrayList_assignVersion(versionArr, celix_version_create(1, 2, 3, 
"qualifier"));
+    celix_arrayList_assignVersion(versionArr, celix_version_create(4, 5, 6, 
"qualifier"));
+    celix_properties_assignArrayList(props, "array/versionArr", versionArr);
+  
+    celix_properties_saveToStream(props, stdout, 
CELIX_PROPERTIES_ENCODE_PRETTY);
+}
+```
+
+Will output the following JSON (order of keys can differ):
+
+```JSON
+{
+  "array/doubleArr": [
+    1.0,
+    2.0
+  ],
+  "array/boolArr": [
+    true,
+    false
+  ],
+  "single/versionKey": "version<1.2.3.qualifier>",
+  "array/longArr": [
+    1,
+    2
+  ],
+  "single/strKey": "strValue",
+  "single/doubleKey": 2.0,
+  "single/boolKey": true,
+  "array/versionArr": [
+    "version<1.2.3.qualifier>",
+    "version<4.5.6.qualifier>"
+  ],
+  "array/stringArr": [
+    "value1",
+    "value2"
+  ],
+  "single/longKey": 42
+}   
+```
+
+### Properties Nested Encoding
+
+When properties are encoded to JSON in a nested structure, the keys of the 
properties are used to create a nested JSON
+object. This is done by using the '/' character in the properties key to 
create a nested JSON objects. When encoding
+properties using a nested structure, there is a risk of key collisions. To 
detect key collisions, the flag
+`CELIX_PROPERTIES_ENCODE_ERROR_ON_COLLISIONS` can be used.
+
+Nested Encoding example:
+
+```C
+#include <stdio.h>
+#include <celix/properties.h>
+
+int main() {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+    celix_properties_set(props, "single/strKey", "strValue");
+    celix_properties_setLong(props, "single/longKey", 42);
+    celix_properties_setDouble(props, "single/doubleKey", 2.0);
+    celix_properties_setBool(props, "single/boolKey", true);
+    celix_properties_assignVersion(props, "single/versionKey", 
celix_version_create(1, 2, 3, "qualifier"));
+
+    celix_array_list_t* strArr = celix_arrayList_createStringArray();
+    celix_arrayList_addString(strArr, "value1");
+    celix_arrayList_addString(strArr, "value2");
+    celix_properties_assignArrayList(props, "array/stringArr", strArr);
+
+    celix_array_list_t* longArr = celix_arrayList_createLongArray();
+    celix_arrayList_addLong(longArr, 1);
+    celix_arrayList_addLong(longArr, 2);
+    celix_properties_assignArrayList(props, "array/longArr", longArr);
+
+    celix_array_list_t* doubleArr = celix_arrayList_createDoubleArray();
+    celix_arrayList_addDouble(doubleArr, 1.0);
+    celix_arrayList_addDouble(doubleArr, 2.0);
+    celix_properties_assignArrayList(props, "array/doubleArr", doubleArr);
+
+    celix_array_list_t* boolArr = celix_arrayList_createBoolArray();
+    celix_arrayList_addBool(boolArr, true);
+    celix_arrayList_addBool(boolArr, false);
+    celix_properties_assignArrayList(props, "array/boolArr", boolArr);
+
+    celix_array_list_t* versionArr = celix_arrayList_createVersionArray();
+    celix_arrayList_assignVersion(versionArr, celix_version_create(1, 2, 3, 
"qualifier"));
+    celix_arrayList_assignVersion(versionArr, celix_version_create(4, 5, 6, 
"qualifier"));
+    celix_properties_assignArrayList(props, "array/versionArr", versionArr);
+  
+    celix_properties_saveToStream(props, stdout, 
CELIX_PROPERTIES_ENCODE_PRETTY | CELIX_PROPERTIES_ENCODE_NESTED_STYLE);
+}
+```
+
+Will output the following JSON (order of keys can differ):
+
+```JSON
+{
+  "array": {
+    "doubleArr": [
+      1.0,
+      2.0
+    ],
+    "boolArr": [
+      true,
+      false
+    ],
+    "longArr": [
+      1,
+      2
+    ],
+    "versionArr": [
+      "version<1.2.3.qualifier>",
+      "version<4.5.6.qualifier>"
+    ],
+    "stringArr": [
+      "value1",
+      "value2"
+    ]
+  },
+  "single": {
+    "versionKey": "version<1.2.3.qualifier>",
+    "strKey": "strValue",
+    "doubleKey": 2.0,
+    "boolKey": true,
+    "longKey": 42
+  }
+}
+```
+
+### Encoding Flags
+
+Properties encoding flags can be used control the behavior of the encoding. 
The following encoding flags can be used:
+
+- `CELIX_PROPERTIES_ENCODE_PRETTY`: Flag to indicate that the encoded output 
should be pretty; e.g. encoded with
+  additional whitespaces, newlines and indentation. If this flag is not set, 
the encoded output will compact; e.g.
+  without additional whitespaces, newlines and indentation.
+
+- `CELIX_PROPERTIES_ENCODE_FLAT_STYLE`: Flag to indicate that the encoded 
output should be flat; e.g. all properties
+  entries are written as top level field entries.
+
+- `CELIX_PROPERTIES_ENCODE_NESTED_STYLE`: Flag to indicate that the encoded 
output should be nested; e.g. properties
+  entries are split on '/' and nested in JSON objects.
+
+- `CELIX_PROPERTIES_ENCODE_ERROR_ON_COLLISIONS`: Flag to indicate that the 
encoding should fail if the JSON
+  representation will contain colliding keys. Note that colliding keys can 
only occur when using the nested encoding
+  style.
+
+- `CELIX_PROPERTIES_ENCODE_ERROR_ON_EMPTY_ARRAYS`: Flag to indicate that the 
encoding should fail if the JSON
+  representation will contain empty arrays.
+
+- `CELIX_PROPERTIES_ENCODE_ERROR_ON_NAN_INF`: Flag to indicate that the 
encoding should fail if the JSON representation
+  will contain NaN or Inf values.
+
+- `CELIX_PROPERTIES_ENCODE_STRICT`: Flag to indicate that all encode "error 
on" flags should be set.
+
+## Properties Decoding
+
+JSON can be decoded to an Apache Celix properties object using
+the `celix_properties_load2`, `celix_properties_loadFromStream` and 
`celix_properties_loadFromString2` functions. These
+functions take a JSON input and decode it to a properties object. Because 
properties use a flat key structure,
+decoding a nested JSON object to properties results in combining JSON object 
keys to a flat key structure. This can
+result in key collisions. 
+
+By default, the decoding will not fail on empty arrays, null values, empty 
keys, or mixed arrays and instead these JSON
+entries will be ignored. Also by default, if decoding results in a duplicate 
properties key, the last value will be used
+and no error will be returned.
+
+### Decoding example
+
+Given a `example.json` file with the following content:
+
+```JSON
+{
+  "counters": {
+    "counter1": 1,
+    "counter2": 2
+  },
+  "strings": {
+    "string1": "value1",
+    "string2": "value2"
+  }
+}
+```
+
+Combined with the following code:
+
+```c
+#include <stdio.h>
+
+#include <celix/properties.h>
+
+int main() {
+    celix_autoptr(celix_properties_t) props;
+    celix_status_t status = celix_properties_load2("example.json", 0, &props):
+    (void)status; //for production code check status
+    CELIX_PROPERTIES_ITERATE(props, iter) { 
+        printf("key=%s, value=%s\n", celix_properties_key(iter.key), 
celix_properties_value(iter.entry.value));        
+    }
+}
+```
+
+Will output the following:
+
+```
+key=counters/counter1, value=1
+key=counters/counter2, value=2
+key=strings/string1, value=value1
+key=strings/string2, value=value2
+```
+
+### Decoding Flags
+
+Properties decoding behavior can be controlled using flags. The following 
decoding flags can be used:
+
+- `CELIX_PROPERTIES_DECODE_ERROR_ON_DUPLICATES`: Flag to indicate that the 
decoding should fail if the input contains
+  duplicate JSON keys.
+
+- `CELIX_PROPERTIES_DECODE_ERROR_ON_COLLISIONS`: Flag to indicate that the 
decoding should fail if the input contains
+  entry that collide on property keys.
+
+- `CELIX_PROPERTIES_DECODE_ERROR_ON_NULL_VALUES`: Flag to indicate that the 
decoding should fail if the input contains
+  null values.
+
+- `CELIX_PROPERTIES_DECODE_ERROR_ON_EMPTY_ARRAYS`: Flag to indicate that the 
decoding should fail if the input contains
+  empty arrays.
+
+- `CELIX_PROPERTIES_DECODE_ERROR_ON_EMPTY_KEYS`: Flag to indicate that the 
decoding should fail if the input contains
+  empty keys.
+
+- `CELIX_PROPERTIES_DECODE_ERROR_ON_MIXED_ARRAYS`: Flag to indicate that the 
decoding should fail if the input contains
+  mixed arrays.
+
+- `CELIX_PROPERTIES_DECODE_STRICT`: Flag to indicate that the decoding should 
fail if the input contains any of the
+  decode error flags.

Reply via email to