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

fokko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 5aed8f5  chore: better error handling of nlohmann json lib apis (#95)
5aed8f5 is described below

commit 5aed8f590ab95866442638a21444c63c0f050e4d
Author: Junwang Zhao <[email protected]>
AuthorDate: Mon May 12 16:46:59 2025 +0800

    chore: better error handling of nlohmann json lib apis (#95)
    
    1. switch off exceptions for nlohmann::json::parse
    
    2. add a no except wrapper around json.dump, since we don't handle
    exceptions in Error messages.
    
    This closes issue #87
    
    References:
    
    - https://json.nlohmann.me/features/parsing/parse_exceptions/
    - https://json.nlohmann.me/api/basic_json/dump/
    
    ---------
    
    Signed-off-by: Junwang Zhao <[email protected]>
---
 src/iceberg/json_internal.cc | 47 +++++++++++++++++++++++++-------------------
 1 file changed, 27 insertions(+), 20 deletions(-)

diff --git a/src/iceberg/json_internal.cc b/src/iceberg/json_internal.cc
index e19f5b4..2ffc543 100644
--- a/src/iceberg/json_internal.cc
+++ b/src/iceberg/json_internal.cc
@@ -22,7 +22,6 @@
 #include <algorithm>
 #include <cstdint>
 #include <format>
-#include <ranges>
 #include <regex>
 #include <type_traits>
 #include <unordered_set>
@@ -37,7 +36,6 @@
 #include "iceberg/snapshot.h"
 #include "iceberg/sort_order.h"
 #include "iceberg/statistics_file.h"
-#include "iceberg/table.h"
 #include "iceberg/table_metadata.h"
 #include "iceberg/transform.h"
 #include "iceberg/type.h"
@@ -173,12 +171,18 @@ void SetOptionalField(nlohmann::json& json, 
std::string_view key,
   }
 }
 
+std::string SafeDumpJson(const nlohmann::json& json) {
+  return json.dump(/*indent=*/-1, /*indent_char=*/' ', /*ensure_ascii=*/false,
+                   nlohmann::detail::error_handler_t::ignore);
+}
+
 template <typename T>
 Result<T> GetJsonValueImpl(const nlohmann::json& json, std::string_view key) {
   try {
     return json.at(key).get<T>();
   } catch (const std::exception& ex) {
-    return JsonParseError("Failed to parse key '{}' in {}", key, json.dump());
+    return JsonParseError("Failed to parse '{}' from {}: {}", key, 
SafeDumpJson(json),
+                          ex.what());
   }
 }
 
@@ -194,7 +198,7 @@ Result<std::optional<T>> GetJsonValueOptional(const 
nlohmann::json& json,
 template <typename T>
 Result<T> GetJsonValue(const nlohmann::json& json, std::string_view key) {
   if (!json.contains(key)) {
-    return JsonParseError("Missing '{}' in {}", key, json.dump());
+    return JsonParseError("Missing '{}' in {}", key, SafeDumpJson(json));
   }
   return GetJsonValueImpl<T>(json, key);
 }
@@ -245,7 +249,7 @@ Result<std::vector<T>> FromJsonList(
     ICEBERG_ASSIGN_OR_RAISE(auto list_json, GetJsonValue<nlohmann::json>(json, 
key));
     if (!list_json.is_array()) {
       return JsonParseError("Cannot parse '{}' from non-array: {}", key,
-                            list_json.dump());
+                            SafeDumpJson(list_json));
     }
     for (const auto& entry_json : list_json) {
       ICEBERG_ASSIGN_OR_RAISE(auto entry, from_json(entry_json));
@@ -270,7 +274,7 @@ Result<std::vector<std::shared_ptr<T>>> FromJsonList(
     ICEBERG_ASSIGN_OR_RAISE(auto list_json, GetJsonValue<nlohmann::json>(json, 
key));
     if (!list_json.is_array()) {
       return JsonParseError("Cannot parse '{}' from non-array: {}", key,
-                            list_json.dump());
+                            SafeDumpJson(list_json));
     }
     for (const auto& entry_json : list_json) {
       ICEBERG_ASSIGN_OR_RAISE(auto entry, from_json(entry_json));
@@ -319,7 +323,7 @@ Result<std::unordered_map<std::string, T>> FromJsonMap(
       try {
         return json.get<std::string>();
       } catch (const std::exception& ex) {
-        return JsonParseError("Cannot parse {} to a string value: {}", 
json.dump(),
+        return JsonParseError("Cannot parse {} to a string value: {}", 
SafeDumpJson(json),
                               ex.what());
       }
     }) {
@@ -328,7 +332,7 @@ Result<std::unordered_map<std::string, T>> FromJsonMap(
     ICEBERG_ASSIGN_OR_RAISE(auto map_json, GetJsonValue<nlohmann::json>(json, 
key));
     if (!map_json.is_object()) {
       return JsonParseError("Cannot parse '{}' from non-object: {}", key,
-                            map_json.dump());
+                            SafeDumpJson(map_json));
     }
     for (const auto& [key, value] : map_json.items()) {
       ICEBERG_ASSIGN_OR_RAISE(auto entry, from_json(value));
@@ -633,7 +637,7 @@ Result<std::unique_ptr<Schema>> SchemaFromJson(const 
nlohmann::json& json) {
   ICEBERG_ASSIGN_OR_RAISE(auto type, TypeFromJson(json));
 
   if (type->type_id() != TypeId::kStruct) [[unlikely]] {
-    return JsonParseError("Schema must be a struct type, but got {}", 
json.dump());
+    return JsonParseError("Schema must be a struct type, but got {}", 
SafeDumpJson(json));
   }
 
   auto& struct_type = static_cast<StructType&>(*type);
@@ -741,11 +745,12 @@ Result<std::unique_ptr<Snapshot>> SnapshotFromJson(const 
nlohmann::json& json) {
         return JsonParseError("Invalid snapshot summary field: {}", key);
       }
       if (!value.is_string()) {
-        return JsonParseError("Invalid snapshot summary field value: {}", 
value.dump());
+        return JsonParseError("Invalid snapshot summary field value: {}",
+                              SafeDumpJson(value));
       }
       if (key == SnapshotSummaryFields::kOperation &&
           !kValidDataOperation.contains(value.get<std::string>())) {
-        return JsonParseError("Invalid snapshot operation: {}", value.dump());
+        return JsonParseError("Invalid snapshot operation: {}", 
SafeDumpJson(value));
       }
       summary[key] = value.get<std::string>();
     }
@@ -975,7 +980,7 @@ Result<std::shared_ptr<Schema>> ParseSchemas(
                             GetJsonValue<nlohmann::json>(json, kSchemas));
     if (!schema_array.is_array()) {
       return JsonParseError("Cannot parse schemas from non-array: {}",
-                            schema_array.dump());
+                            SafeDumpJson(schema_array));
     }
 
     ICEBERG_ASSIGN_OR_RAISE(current_schema_id,
@@ -990,7 +995,7 @@ Result<std::shared_ptr<Schema>> ParseSchemas(
     }
     if (!current_schema) {
       return JsonParseError("Cannot find schema with {}={} from {}", 
kCurrentSchemaId,
-                            current_schema_id.value(), schema_array.dump());
+                            current_schema_id.value(), 
SafeDumpJson(schema_array));
     }
   } else {
     if (format_version != 1) {
@@ -1021,7 +1026,7 @@ Status ParsePartitionSpecs(const nlohmann::json& json, 
int8_t format_version,
                             GetJsonValue<nlohmann::json>(json, 
kPartitionSpecs));
     if (!spec_array.is_array()) {
       return JsonParseError("Cannot parse partition specs from non-array: {}",
-                            spec_array.dump());
+                            SafeDumpJson(spec_array));
     }
     ICEBERG_ASSIGN_OR_RAISE(default_spec_id, GetJsonValue<int32_t>(json, 
kDefaultSpecId));
 
@@ -1040,7 +1045,7 @@ Status ParsePartitionSpecs(const nlohmann::json& json, 
int8_t format_version,
                             GetJsonValue<nlohmann::json>(json, 
kPartitionSpec));
     if (!partition_spec_json.is_array()) {
       return JsonParseError("Cannot parse v1 partition spec from non-array: 
{}",
-                            partition_spec_json.dump());
+                            SafeDumpJson(partition_spec_json));
     }
 
     int32_t next_partition_field_id = 
PartitionSpec::kLegacyPartitionDataIdStart;
@@ -1098,7 +1103,8 @@ Status ParseSortOrders(const nlohmann::json& json, int8_t 
format_version,
 
 Result<std::unique_ptr<TableMetadata>> TableMetadataFromJson(const 
nlohmann::json& json) {
   if (!json.is_object()) {
-    return JsonParseError("Cannot parse metadata from a non-object: {}", 
json.dump());
+    return JsonParseError("Cannot parse metadata from a non-object: {}",
+                          SafeDumpJson(json));
   }
 
   auto table_metadata = std::make_unique<TableMetadata>();
@@ -1210,11 +1216,12 @@ Result<std::unique_ptr<TableMetadata>> 
TableMetadataFromJson(const nlohmann::jso
 }
 
 Result<nlohmann::json> FromJsonString(const std::string& json_string) {
-  try {
-    return nlohmann::json::parse(json_string);
-  } catch (const std::exception& e) {
-    return JsonParseError("Failed to parse JSON string: {}", e.what());
+  auto json =
+      nlohmann::json::parse(json_string, /*cb=*/nullptr, 
/*allow_exceptions=*/false);
+  if (json.is_discarded()) [[unlikely]] {
+    return JsonParseError("Failed to parse JSON string: {}", json_string);
   }
+  return json;
 }
 
 Result<std::string> ToJsonString(const nlohmann::json& json) {

Reply via email to