Skip to content

Commit b85477b

Browse files
authored
refactor: extract JSON utilities for code reuse (#249)
1 parent 3973bef commit b85477b

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
@@ -41,6 +41,7 @@
4141
#include "iceberg/transform.h"
4242
#include "iceberg/type.h"
4343
#include "iceberg/util/formatter.h" // IWYU pragma: keep
44+
#include "iceberg/util/json_util_internal.h"
4445
#include "iceberg/util/macros.h"
4546
#include "iceberg/util/timepoint.h"
4647

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

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

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

0 commit comments

Comments
 (0)