Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 130 additions & 32 deletions include/vix/json/dumps.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,46 +69,60 @@ namespace vix::json
namespace fs = std::filesystem;

/**
* @brief Serialize JSON to a human-readable string (pretty printed).
* @brief Reusable JSON serialization settings for string and file output.
*
* @param j JSON value (object/array/etc).
* @param indent Number of spaces per indentation level (default: 2).
* @param ensure_ascii If true, non-ASCII characters are escaped.
* @return Formatted JSON string.
* @details
* - `pretty=true` enables multi-line formatted output using `indent`.
* - `pretty=false` enables compact single-line output and ignores `indent`.
* - `indent` must be non-negative.
* - `ensure_ascii=true` escapes non-ASCII characters.
*/
struct SerializeOptions
{
bool pretty = true;
int indent = 2;
bool ensure_ascii = false;
};

/**
* @brief Validate serialization options before use.
*
* @note
* Use this for logs, debugging, configuration files, and readable outputs.
* @throws std::invalid_argument If `indent` is negative.
*/
inline std::string dumps(const Json &j, int indent = 2, bool ensure_ascii = false)
inline void validate_serialize_options(const SerializeOptions &options)
{
return j.dump(indent, ' ', ensure_ascii);
if (options.indent < 0)
throw std::invalid_argument("JSON serialization indentation must be non-negative");
}

/**
* @brief Serialize JSON to a compact single-line string.
* @brief Resolve the effective indent passed to nlohmann::json::dump().
*
* @param j JSON value (object/array/etc).
* @param ensure_ascii If true, non-ASCII characters are escaped.
* @return Compact JSON string.
*
* @note
* Use this for network payloads, compact storage, or performance-sensitive logs.
* @details
* Compact mode ignores `indent` and always produces a single-line string.
*/
inline std::string dumps_compact(const Json &j, bool ensure_ascii = false)
inline int resolve_dump_indent(const SerializeOptions &options)
{
return j.dump(-1, ' ', ensure_ascii);
return options.pretty ? options.indent : -1;
}

/**
* @brief Explicit alias for dumps() to emphasize readability.
* @brief Serialize JSON using explicit serialization options.
*
* @param j JSON value (object/array/etc).
* @param options Serialization settings shared with file output.
* @return Serialized JSON string.
*
* @throws std::invalid_argument If `options.indent` is negative.
*/
inline std::string dumps_pretty(const Json &j, int indent = 2, bool ensure_ascii = false)
inline std::string dumps(const Json &j, const SerializeOptions &options)
{
return dumps(j, indent, ensure_ascii);
validate_serialize_options(options);
return j.dump(resolve_dump_indent(options), ' ', options.ensure_ascii);
}

/**
* @brief Write JSON to a file using a temp file + rename strategy.
* @brief Write JSON to a file using explicit serialization options.
*
* @details
* Steps:
Expand All @@ -117,22 +131,19 @@ namespace vix::json
* 3) Replace destination by renaming temp file to `<path>`.
* 4) If rename fails, fallback to copy + remove.
*
* This strategy reduces the chance of ending with a partially-written file.
*
* @param path Destination file path.
* @param j JSON value to write.
* @param indent Pretty-print indentation (default: 2).
* @param ensure_ascii Escape non-ASCII characters if true.
* @param options Serialization settings shared with string output.
*
* @throws std::invalid_argument
* If `options.indent` is negative.
* @throws std::runtime_error
* If the temp file cannot be written or the final replacement fails.
*
* @warning
* This function writes the entire JSON document at once.
* Do not use it for unbounded user uploads.
*/
inline void dump_file(const fs::path &path, const Json &j, int indent = 2, bool ensure_ascii = false)
inline void dump_file(const fs::path &path, const Json &j, const SerializeOptions &options)
{
validate_serialize_options(options);

// 1) Ensure directory exists (best-effort)
if (path.has_parent_path())
{
Expand All @@ -151,7 +162,7 @@ namespace vix::json

try
{
const auto s = j.dump(indent, ' ', ensure_ascii);
const auto s = dumps(j, options);
ofs.write(s.data(), static_cast<std::streamsize>(s.size()));
ofs.flush();
if (!ofs)
Expand Down Expand Up @@ -182,18 +193,105 @@ namespace vix::json
}
}

/**
* @brief Serialize JSON to a human-readable string (pretty printed).
*
* @param j JSON value (object/array/etc).
* @param indent Number of spaces per indentation level (default: 2).
* @param ensure_ascii If true, non-ASCII characters are escaped.
* @return Formatted JSON string.
*
* @note
* Use this for logs, debugging, configuration files, and readable outputs.
*/
inline std::string dumps(const Json &j, int indent = 2, bool ensure_ascii = false)
{
if (indent < 0)
return dumps(j, SerializeOptions{false, 2, ensure_ascii});
return dumps(j, SerializeOptions{true, indent, ensure_ascii});
}

/**
* @brief Serialize JSON to a compact single-line string.
*
* @param j JSON value (object/array/etc).
* @param ensure_ascii If true, non-ASCII characters are escaped.
* @return Compact JSON string.
*
* @note
* Use this for network payloads, compact storage, or performance-sensitive logs.
*/
inline std::string dumps_compact(const Json &j, bool ensure_ascii = false)
{
return dumps(j, SerializeOptions{false, 2, ensure_ascii});
}

/**
* @brief Explicit alias for dumps() to emphasize readability.
*/
inline std::string dumps_pretty(const Json &j, int indent = 2, bool ensure_ascii = false)
{
return dumps(j, indent, ensure_ascii);
}

/**
* @brief Write JSON to a file using a temp file + rename strategy.
*
* @details
* Steps:
* 1) Ensure parent directory exists (best-effort).
* 2) Write JSON to `<path>.tmp`.
* 3) Replace destination by renaming temp file to `<path>`.
* 4) If rename fails, fallback to copy + remove.
*
* This strategy reduces the chance of ending with a partially-written file.
*
* @param path Destination file path.
* @param j JSON value to write.
* @param indent Pretty-print indentation (default: 2).
* @param ensure_ascii Escape non-ASCII characters if true.
*
* @throws std::runtime_error
* If the temp file cannot be written or the final replacement fails.
*
* @warning
* This function writes the entire JSON document at once.
* Do not use it for unbounded user uploads.
*/
inline void dump_file(const fs::path &path, const Json &j, int indent = 2, bool ensure_ascii = false)
{
if (indent < 0)
{
dump_file(path, j, SerializeOptions{false, 2, ensure_ascii});
return;
}
dump_file(path, j, SerializeOptions{true, indent, ensure_ascii});
}

/// @overload Convenience overload for C-string path.
inline void dump_file(const char *path, const Json &j, int indent = 2, bool ensure_ascii = false)
{
dump_file(fs::path{path}, j, indent, ensure_ascii);
}

/// @overload Convenience overload for C-string path with explicit options.
inline void dump_file(const char *path, const Json &j, const SerializeOptions &options)
{
dump_file(fs::path{path}, j, options);
}

/// @overload Convenience overload for std::string path.
inline void dump_file(const std::string &path, const Json &j, int indent = 2, bool ensure_ascii = false)
{
dump_file(fs::path{path}, j, indent, ensure_ascii);
}

/// @overload Convenience overload for std::string path with explicit options.
inline void dump_file(const std::string &path, const Json &j, const SerializeOptions &options)
{
dump_file(fs::path{path}, j, options);
}

} // namespace vix::json

#endif // VIX_JSON_DUMPS_HPP
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(VIX_JSON_TEST_TARGETS
json_build_test
json_convert_test
json_dumps_test
json_dumps_options_test
json_include_test
json_jpath_test
json_loads_test
Expand Down
Loading