diff --git a/src/iceberg/CMakeLists.txt b/src/iceberg/CMakeLists.txt index 35c312f60..7e035cd02 100644 --- a/src/iceberg/CMakeLists.txt +++ b/src/iceberg/CMakeLists.txt @@ -31,6 +31,7 @@ set(ICEBERG_SOURCES expression/expression.cc expression/expressions.cc expression/inclusive_metrics_evaluator.cc + expression/json_serde.cc expression/literal.cc expression/manifest_evaluator.cc expression/predicate.cc diff --git a/src/iceberg/expression/json_serde.cc b/src/iceberg/expression/json_serde.cc new file mode 100644 index 000000000..084695ced --- /dev/null +++ b/src/iceberg/expression/json_serde.cc @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include + +#include "iceberg/expression/expressions.h" +#include "iceberg/expression/json_serde_internal.h" +#include "iceberg/expression/literal.h" +#include "iceberg/util/json_util_internal.h" +#include "iceberg/util/macros.h" + +namespace iceberg { + +Result> ExpressionFromJson(const nlohmann::json& json) { + // Handle boolean + if (json.is_boolean()) { + return json.get() ? std::static_pointer_cast(True::Instance()) + : std::static_pointer_cast(False::Instance()); + } + return JsonParseError("Only boolean are currently supported"); +} + +nlohmann::json ToJson(const Expression& expr) { + switch (expr.op()) { + case Expression::Operation::kTrue: + return true; + + case Expression::Operation::kFalse: + return false; + default: + throw std::logic_error("Only booleans are currently supported"); + } +} + +#define ICEBERG_DEFINE_FROM_JSON(Model) \ + template <> \ + Result> FromJson(const nlohmann::json& json) { \ + return Model##FromJson(json); \ + } + +ICEBERG_DEFINE_FROM_JSON(Expression) + +} // namespace iceberg diff --git a/src/iceberg/expression/json_serde_internal.h b/src/iceberg/expression/json_serde_internal.h new file mode 100644 index 000000000..aea805cec --- /dev/null +++ b/src/iceberg/expression/json_serde_internal.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#pragma once + +#include + +#include "iceberg/expression/expression.h" +#include "iceberg/iceberg_export.h" +#include "iceberg/result.h" + +/// \file iceberg/expression/json_internal.h +/// JSON serialization and deserialization for expressions. + +namespace iceberg { + +template +Result FromJson(const nlohmann::json& json); + +#define ICEBERG_DECLARE_JSON_SERDE(Model) \ + ICEBERG_EXPORT Result> Model##FromJson( \ + const nlohmann::json& json); \ + \ + template \ + ICEBERG_EXPORT Result> FromJson(const nlohmann::json& json); \ + \ + ICEBERG_EXPORT nlohmann::json ToJson(const Model& model); + +/// \note Don't forget to add `ICEBERG_DEFINE_FROM_JSON` to the end of +/// `json_internal.cc` to define the `FromJson` function for the model. +ICEBERG_DECLARE_JSON_SERDE(Expression) + +#undef ICEBERG_DECLARE_JSON_SERDE + +} // namespace iceberg diff --git a/src/iceberg/test/CMakeLists.txt b/src/iceberg/test/CMakeLists.txt index d243a48bf..00fff9f27 100644 --- a/src/iceberg/test/CMakeLists.txt +++ b/src/iceberg/test/CMakeLists.txt @@ -88,6 +88,7 @@ add_iceberg_test(table_test add_iceberg_test(expression_test SOURCES aggregate_test.cc + expression_json_test.cc expression_test.cc expression_visitor_test.cc inclusive_metrics_evaluator_test.cc diff --git a/src/iceberg/test/expression_json_test.cc b/src/iceberg/test/expression_json_test.cc new file mode 100644 index 000000000..5af23236b --- /dev/null +++ b/src/iceberg/test/expression_json_test.cc @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include +#include +#include + +#include "iceberg/expression/expression.h" +#include "iceberg/expression/expressions.h" +#include "iceberg/expression/json_serde_internal.h" +#include "iceberg/expression/literal.h" +#include "iceberg/expression/predicate.h" +#include "iceberg/expression/term.h" +#include "iceberg/test/matchers.h" + +namespace iceberg { + +class ExpressionJsonTest : public ::testing::Test {}; + +// Test boolean constant expressions +TEST_F(ExpressionJsonTest, TrueExpression) { + auto expr = True::Instance(); + auto json = ToJson(*expr); + + // True should serialize as JSON boolean true + EXPECT_TRUE(json.is_boolean()); + EXPECT_TRUE(json.get()); + + // Parse back + auto result = ExpressionFromJson(json); + ASSERT_THAT(result, IsOk()); + EXPECT_EQ(result.value()->op(), Expression::Operation::kTrue); +} + +TEST_F(ExpressionJsonTest, FalseExpression) { + auto expr = False::Instance(); + auto json = ToJson(*expr); + + // False should serialize as JSON boolean false + EXPECT_TRUE(json.is_boolean()); + EXPECT_FALSE(json.get()); + + // Parse back + auto result = ExpressionFromJson(json); + ASSERT_THAT(result, IsOk()); + EXPECT_EQ(result.value()->op(), Expression::Operation::kFalse); +} + +} // namespace iceberg