Skip to content

Commit 1305509

Browse files
authored
AVRO-4026: [C++] Allow non-string custom attributes (#3344)
1 parent 7b106b1 commit 1305509

4 files changed

Lines changed: 76 additions & 5 deletions

File tree

lang/c++/impl/Compiler.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ static void getCustomAttributes(const Object &m, CustomAttributes &customAttribu
291291
const std::unordered_set<std::string> &kKnownFields = getKnownFields();
292292
for (const auto &entry : m) {
293293
if (kKnownFields.find(entry.first) == kKnownFields.end()) {
294-
customAttributes.addAttribute(entry.first, entry.second.toLiteralString());
294+
bool addQuotes = entry.second.type() == json::EntityType::String;
295+
customAttributes.addAttribute(entry.first, entry.second.toLiteralString(), addQuotes);
295296
}
296297
}
297298
}

lang/c++/impl/CustomAttributes.cc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,28 @@ std::optional<std::string> CustomAttributes::getAttribute(const std::string &nam
3535
}
3636

3737
void CustomAttributes::addAttribute(const std::string &name,
38-
const std::string &value) {
38+
const std::string &value,
39+
bool addQuotes) {
3940
auto iter_and_find =
4041
attributes_.insert(std::pair<std::string, std::string>(name, value));
4142
if (!iter_and_find.second) {
4243
throw Exception(name + " already exists and cannot be added");
4344
}
45+
if (addQuotes) {
46+
keysNeedQuotes_.insert(name);
47+
}
4448
}
4549

4650
void CustomAttributes::printJson(std::ostream &os,
4751
const std::string &name) const {
48-
if (attributes().find(name) == attributes().end()) {
52+
auto iter = attributes_.find(name);
53+
if (iter == attributes_.cend()) {
4954
throw Exception(name + " doesn't exist");
5055
}
51-
os << "\"" << name << "\": \"" << attributes().at(name) << "\"";
56+
if (keysNeedQuotes_.find(name) != keysNeedQuotes_.cend()) {
57+
os << "\"" << name << "\": \"" << iter->second << "\"";
58+
} else {
59+
os << "\"" << name << "\": " << iter->second;
60+
}
5261
}
5362
} // namespace avro

lang/c++/include/avro/CustomAttributes.hh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <map>
2525
#include <optional>
2626
#include <string>
27+
#include <unordered_set>
2728

2829
namespace avro {
2930

@@ -37,7 +38,10 @@ public:
3738
std::optional<std::string> getAttribute(const std::string &name) const;
3839

3940
// Adds a custom attribute. If the attribute already exists, throw an exception.
40-
void addAttribute(const std::string &name, const std::string &value);
41+
//
42+
// If `addQuotes` is true, the `value` will be wrapped in double quotes in the
43+
// json serialization; otherwise, the `value` will be serialized as is.
44+
void addAttribute(const std::string &name, const std::string &value, bool addQuotes = true);
4145

4246
// Provides a way to iterate over the custom attributes or check attribute size.
4347
const std::map<std::string, std::string> &attributes() const {
@@ -49,6 +53,7 @@ public:
4953

5054
private:
5155
std::map<std::string, std::string> attributes_;
56+
std::unordered_set<std::string> keysNeedQuotes_;
5257
};
5358

5459
} // namespace avro

lang/c++/test/SchemaTests.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,60 @@ static void testAddCustomAttributes() {
856856
BOOST_CHECK_EQUAL(removeWhitespaceFromSchema(json), removeWhitespaceFromSchema(expected));
857857
}
858858

859+
static void testCustomAttributesJson2Schema2Json() {
860+
const std::string schema = R"({
861+
"type": "record",
862+
"name": "my_record",
863+
"fields": [
864+
{ "name": "long_field", "type": "long", "int_key": 1, "str_key": "1" }
865+
]
866+
})";
867+
ValidSchema compiledSchema = compileJsonSchemaFromString(schema);
868+
869+
// Verify custom attributes from parsed schema
870+
auto customAttributes = compiledSchema.root()->customAttributesAt(0);
871+
BOOST_CHECK_EQUAL(customAttributes.getAttribute("int_key").value(), "1");
872+
BOOST_CHECK_EQUAL(customAttributes.getAttribute("str_key").value(), "1");
873+
874+
// Verify custom attributes from json result
875+
std::string json = compiledSchema.toJson();
876+
BOOST_CHECK_EQUAL(removeWhitespaceFromSchema(json), removeWhitespaceFromSchema(schema));
877+
}
878+
879+
static void testCustomAttributesSchema2Json2Schema() {
880+
const std::string expected = R"({
881+
"type": "record",
882+
"name": "my_record",
883+
"fields": [
884+
{ "name": "long_field", "type": "long", "int_key": 1, "str_key": "1" }
885+
]
886+
})";
887+
888+
auto recordNode = std::make_shared<NodeRecord>();
889+
{
890+
CustomAttributes customAttributes;
891+
customAttributes.addAttribute("int_key", "1", /*addQuotes=*/false);
892+
customAttributes.addAttribute("str_key", "1", /*addQuotes=*/true);
893+
recordNode->addCustomAttributesForField(customAttributes);
894+
recordNode->addLeaf(std::make_shared<NodePrimitive>(AVRO_LONG));
895+
recordNode->addName("long_field");
896+
recordNode->setName(Name("my_record"));
897+
}
898+
899+
// Verify custom attributes from json result
900+
ValidSchema schema(recordNode);
901+
std::string json = schema.toJson();
902+
BOOST_CHECK_EQUAL(removeWhitespaceFromSchema(json), removeWhitespaceFromSchema(expected));
903+
904+
// Verify custom attributes from parsed schema
905+
{
906+
auto parsedSchema = compileJsonSchemaFromString(json);
907+
auto customAttributes = parsedSchema.root()->customAttributesAt(0);
908+
BOOST_CHECK_EQUAL(customAttributes.getAttribute("int_key").value(), "1");
909+
BOOST_CHECK_EQUAL(customAttributes.getAttribute("str_key").value(), "1");
910+
}
911+
}
912+
859913
} // namespace schema
860914
} // namespace avro
861915

@@ -882,5 +936,7 @@ init_unit_test_suite(int /*argc*/, char * /*argv*/[]) {
882936
ts->add(BOOST_TEST_CASE(&avro::schema::testCustomLogicalType));
883937
ts->add(BOOST_TEST_CASE(&avro::schema::testParseCustomAttributes));
884938
ts->add(BOOST_TEST_CASE(&avro::schema::testAddCustomAttributes));
939+
ts->add(BOOST_TEST_CASE(&avro::schema::testCustomAttributesJson2Schema2Json));
940+
ts->add(BOOST_TEST_CASE(&avro::schema::testCustomAttributesSchema2Json2Schema));
885941
return ts;
886942
}

0 commit comments

Comments
 (0)