Skip to content

Store: store structured data as JSON objects #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

jini-zh
Copy link
Contributor

@jini-zh jini-zh commented Jun 2, 2025

This pull request makes an overhaul of Store implementation, extending its support of edge cases in user data.

The primary change is that the structured data stored in a Store such as vectors and maps, are stored as JSON arrays and objects, with the strings in keys and values properly encoded, so a "q } w" should result the same string when requested, and it won't confuse the JSON parser and serializer.

The type of an object stored is encoded by the first character of its serialized string: " means that it's a string, [ --- a vector, { --- a map. Any other character means that it is an object with custom encoding (see below). Strings and custom objects are stored as is, but when serialized to JSON format, their contents are properly escaped to produce a valid JSON string. Vectors and maps are stored in JSON format initially.

The user can add support for storage of their own classes T by implementing operator<<(std::ostream&, T) and operator>>(std::istream&, T&), or json_encode_r(std::ostream&, T) and json_decode_r(const char*&, T&). When both the operators and the functions are defined, the functions take priority. json_encode_r and json_decode_r extend the JSON parser and serializer to support custom user classes and are useful outside the Store class.

Speaking of JSON, this pull request also adds generic functions for JSON production, validation, and parsing, see json_encode, json_scan* and json_decode in Json.h.

Store::Initialise has been updated to allow escaped comments, newlines, and spaces at the end of a line, and to keep all of the spaces in the value string, so

var long   space \# test \\ \
  newline  \ 

results in "long space # test \\ \n newline ". This change is not backwards compatible, but it is unlikely that anyone relied on that feature.

UnitTests/StoreTest has been rewritten to test for many edge cases and the new features.

I personally think that we should stop abusing Store for JSON data and instead either implement a custom JSON object that stores values as a tree in memory, or just use the new json_encode and json_decode functions for (de-)serialization.

I have checked that libDAQInterface Example works with the new Store, but I think is worth testing this code on some real codebase before merging this pull request. Can someone do that?

This pull request fixes #19, #22, #25. #16 appears to be not relevant anymore.

Evgenii Zhemchugov added 3 commits June 2, 2025 15:43
Implement full-blown JSON serialization and validation functions.
Support extensions for user classes.
* Use JSON encoding for strings with special characters, vectors, and
  maps to ensure that deserialization returns what has been serialized.

* Make JsonParser work for JSON objects containing strings with special
  characters.

* JsonParser now returns a bool indicating whether the parsing was
  successful.

* Replace `template <typename T> void operator>>(T&)` with `void
  operator>>(std::string&)` since it only worked for strings anyways.

* Make `operator>>` produce numbers as JSON numbers.

* Rewrite UnitTests/StoreTest to thoroughly test Store implementation.

The type of the stored object is encoded by the first character in the
stored value: `"` for strings, `[` for arrays, and `{` for objects. A
matching character is expected at the end of the value. Any other
character at the beginning of the value means that the object was
encoded with a custom `operator<<(std::ostream&, T)`, and it is expected
that `operator>>(std::istream&, T&)` can be used to decode the object
completely. If such encoding produces a string beginning with one of the
characters above, it is wrapped into a pair of '"'. These conversions
are handled transparently to the user.
…ew store contents

Calling Initialise on a file setting an integer variable, e.g.,

    var 1

resulted in this variable stored as "1" and interpreted as a string, so
Get<int>("1") returned 0 (json_decode failed to parse "1" as a number).
This is inconsistent both with the previous and the new behaviours where
numbers set with `Set` are stored unquoted. This patch updates
Initialise to store a value as is, unless it begins with a `"`, or it
begins with a `[` or `{` and is not a valid JSON object. In these cases
the value is quoted.

Also:

* Initialise now does not drop the whitespace in the value, so

    var a string with a   tab and a   long space

does not result in `a string with a tab and a long space`.

* Comments, newlines, and spaces at the end can now be escaped:

    escaped_comment test \# test
    escaped_newline test \
    test2
    escaped_whitespace test \

* One backward incompatible change is that a space is now not required
  before an inline comment:

    var val#ue

now results in `val` rather than `val#ue`.

StoreTest has been updated to test for these cases.
@jini-zh jini-zh marked this pull request as ready for review June 4, 2025 14:10
Avoid leaving Store half-filled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Invalid JSON
1 participant