Skip to content

Commit f76a07e

Browse files
committed
refactor: extract JSON utilities for code reuse
1 parent 1cea1e9 commit f76a07e

File tree

2 files changed

+215
-179
lines changed

2 files changed

+215
-179
lines changed

src/iceberg/json_internal.cc

Lines changed: 1 addition & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "iceberg/transform.h"
4343
#include "iceberg/type.h"
4444
#include "iceberg/util/formatter.h" // IWYU pragma: keep
45+
#include "iceberg/util/json_util_internal.h"
4546
#include "iceberg/util/macros.h"
4647
#include "iceberg/util/timepoint.h"
4748

@@ -166,185 +167,6 @@ constexpr std::string_view kFileSizeInBytes = "file-size-in-bytes";
166167
constexpr std::string_view kFileFooterSizeInBytes = "file-footer-size-in-bytes";
167168
constexpr std::string_view kBlobMetadata = "blob-metadata";
168169

169-
template <typename T>
170-
void SetOptionalField(nlohmann::json& json, std::string_view key,
171-
const std::optional<T>& value) {
172-
if (value.has_value()) {
173-
json[key] = *value;
174-
}
175-
}
176-
177-
std::string SafeDumpJson(const nlohmann::json& json) {
178-
return json.dump(/*indent=*/-1, /*indent_char=*/' ', /*ensure_ascii=*/false,
179-
nlohmann::detail::error_handler_t::ignore);
180-
}
181-
182-
template <typename T>
183-
Result<T> GetJsonValueImpl(const nlohmann::json& json, std::string_view key) {
184-
try {
185-
return json.at(key).get<T>();
186-
} catch (const std::exception& ex) {
187-
return JsonParseError("Failed to parse '{}' from {}: {}", key, SafeDumpJson(json),
188-
ex.what());
189-
}
190-
}
191-
192-
template <typename T>
193-
Result<std::optional<T>> GetJsonValueOptional(const nlohmann::json& json,
194-
std::string_view key) {
195-
if (!json.contains(key)) {
196-
return std::nullopt;
197-
}
198-
return GetJsonValueImpl<T>(json, key);
199-
}
200-
201-
template <typename T>
202-
Result<T> GetJsonValue(const nlohmann::json& json, std::string_view key) {
203-
if (!json.contains(key)) {
204-
return JsonParseError("Missing '{}' in {}", key, SafeDumpJson(json));
205-
}
206-
return GetJsonValueImpl<T>(json, key);
207-
}
208-
209-
template <typename T>
210-
Result<T> GetJsonValueOrDefault(const nlohmann::json& json, std::string_view key,
211-
T default_value = T{}) {
212-
if (!json.contains(key)) {
213-
return default_value;
214-
}
215-
return GetJsonValueImpl<T>(json, key);
216-
}
217-
218-
/// \brief Convert a list of items to a json array.
219-
///
220-
/// Note that ToJson(const T&) is required for this function to work.
221-
template <typename T>
222-
nlohmann::json::array_t ToJsonList(const std::vector<T>& list) {
223-
return std::accumulate(list.cbegin(), list.cend(), nlohmann::json::array(),
224-
[](nlohmann::json::array_t arr, const T& item) {
225-
arr.push_back(ToJson(item));
226-
return arr;
227-
});
228-
}
229-
230-
/// \brief Overload of the above function for a list of shared pointers.
231-
template <typename T>
232-
nlohmann::json::array_t ToJsonList(const std::vector<std::shared_ptr<T>>& list) {
233-
return std::accumulate(list.cbegin(), list.cend(), nlohmann::json::array(),
234-
[](nlohmann::json::array_t arr, const std::shared_ptr<T>& item) {
235-
arr.push_back(ToJson(*item));
236-
return arr;
237-
});
238-
}
239-
240-
/// \brief Parse a list of items from a JSON object.
241-
///
242-
/// \param[in] json The JSON object to parse.
243-
/// \param[in] key The key to parse.
244-
/// \param[in] from_json The function to parse an item from a JSON object.
245-
/// \return The list of items.
246-
template <typename T>
247-
Result<std::vector<T>> FromJsonList(
248-
const nlohmann::json& json, std::string_view key,
249-
const std::function<Result<T>(const nlohmann::json&)>& from_json) {
250-
std::vector<T> list{};
251-
if (json.contains(key)) {
252-
ICEBERG_ASSIGN_OR_RAISE(auto list_json, GetJsonValue<nlohmann::json>(json, key));
253-
if (!list_json.is_array()) {
254-
return JsonParseError("Cannot parse '{}' from non-array: {}", key,
255-
SafeDumpJson(list_json));
256-
}
257-
for (const auto& entry_json : list_json) {
258-
ICEBERG_ASSIGN_OR_RAISE(auto entry, from_json(entry_json));
259-
list.emplace_back(std::move(entry));
260-
}
261-
}
262-
return list;
263-
}
264-
265-
/// \brief Parse a list of items from a JSON object.
266-
///
267-
/// \param[in] json The JSON object to parse.
268-
/// \param[in] key The key to parse.
269-
/// \param[in] from_json The function to parse an item from a JSON object.
270-
/// \return The list of items.
271-
template <typename T>
272-
Result<std::vector<std::shared_ptr<T>>> FromJsonList(
273-
const nlohmann::json& json, std::string_view key,
274-
const std::function<Result<std::shared_ptr<T>>(const nlohmann::json&)>& from_json) {
275-
std::vector<std::shared_ptr<T>> list{};
276-
if (json.contains(key)) {
277-
ICEBERG_ASSIGN_OR_RAISE(auto list_json, GetJsonValue<nlohmann::json>(json, key));
278-
if (!list_json.is_array()) {
279-
return JsonParseError("Cannot parse '{}' from non-array: {}", key,
280-
SafeDumpJson(list_json));
281-
}
282-
for (const auto& entry_json : list_json) {
283-
ICEBERG_ASSIGN_OR_RAISE(auto entry, from_json(entry_json));
284-
list.emplace_back(std::move(entry));
285-
}
286-
}
287-
return list;
288-
}
289-
290-
/// \brief Convert a map of type <std::string, T> to a json object.
291-
///
292-
/// Note that ToJson(const T&) is required for this function to work.
293-
template <typename T>
294-
nlohmann::json::object_t ToJsonMap(const std::unordered_map<std::string, T>& map) {
295-
return std::accumulate(map.cbegin(), map.cend(), nlohmann::json::object(),
296-
[](nlohmann::json::object_t obj, const auto& item) {
297-
obj[item.first] = ToJson(item.second);
298-
return obj;
299-
});
300-
}
301-
302-
/// \brief Overload of the above function for a map of type <std::string,
303-
/// std::shared_ptr<T>>.
304-
template <typename T>
305-
nlohmann::json::object_t ToJsonMap(
306-
const std::unordered_map<std::string, std::shared_ptr<T>>& map) {
307-
return std::accumulate(map.cbegin(), map.cend(), nlohmann::json::object(),
308-
[](nlohmann::json::object_t obj, const auto& item) {
309-
obj[item.first] = ToJson(*item.second);
310-
return obj;
311-
});
312-
}
313-
314-
/// \brief Parse a map of type <std::string, T> from a JSON object.
315-
///
316-
/// \param[in] json The JSON object to parse.
317-
/// \param[in] key The key to parse.
318-
/// \param[in] from_json The function to parse an item from a JSON object.
319-
/// \return The map of items.
320-
template <typename T = std::string>
321-
Result<std::unordered_map<std::string, T>> FromJsonMap(
322-
const nlohmann::json& json, std::string_view key,
323-
const std::function<Result<T>(const nlohmann::json&)>& from_json =
324-
[](const nlohmann::json& json) -> Result<T> {
325-
static_assert(std::is_same_v<T, std::string>, "T must be std::string");
326-
try {
327-
return json.get<std::string>();
328-
} catch (const std::exception& ex) {
329-
return JsonParseError("Cannot parse {} to a string value: {}", SafeDumpJson(json),
330-
ex.what());
331-
}
332-
}) {
333-
std::unordered_map<std::string, T> map{};
334-
if (json.contains(key)) {
335-
ICEBERG_ASSIGN_OR_RAISE(auto map_json, GetJsonValue<nlohmann::json>(json, key));
336-
if (!map_json.is_object()) {
337-
return JsonParseError("Cannot parse '{}' from non-object: {}", key,
338-
SafeDumpJson(map_json));
339-
}
340-
for (const auto& [key, value] : map_json.items()) {
341-
ICEBERG_ASSIGN_OR_RAISE(auto entry, from_json(value));
342-
map[key] = std::move(entry);
343-
}
344-
}
345-
return map;
346-
}
347-
348170
} // namespace
349171

350172
nlohmann::json ToJson(const SortField& sort_field) {

0 commit comments

Comments
 (0)