Skip to content

Commit 10306f3

Browse files
committed
Fix JSON string-array serialization in TgTypeParser
parseArray<std::string> call sites passed an identity lambda that returned the raw string unchanged, producing `[a,b]` instead of `["a","b"]`. The resulting invalid JSON broke sticker creation via createNewStickerSet / addStickerToSet (emoji_list and keywords were serialized as naked identifier lists) and Api::setWebhook (allowed_updates missing element quotes). Added a private appendStringArrayToJson helper that emits `"name":["escaped","values"],` directly, escaping each element with StringTools::escapeJsonString. Replaced the seven broken call sites in TgTypeParser (WebhookInfo.allowed_updates, Chat.active_usernames, Giveaway.country_codes, InputSticker.emoji_list, InputSticker.keywords, and both PassportElementError file_hashes variants) and fixed the one remaining lambda site in Api::setWebhook to match the pattern already used by the other Api.cpp call sites. Added three regression tests covering InputSticker serialization. Closes #346
1 parent e79ac64 commit 10306f3

5 files changed

Lines changed: 94 additions & 29 deletions

File tree

include/tgbot/TgTypeParser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,10 @@ class TGBOT_API TgTypeParser {
957957
}
958958

959959
void appendToJson(std::string& json, const std::string& varName, const std::string& value) const;
960+
961+
/// Appends `"varName":["escapedValue1","escapedValue2",...],` to `json`.
962+
/// Does nothing when `values` is empty. Each element is JSON-escaped.
963+
void appendStringArrayToJson(std::string& json, const std::string& varName, const std::vector<std::string>& values) const;
960964
};
961965
}
962966

src/Api.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ bool Api::setWebhook(const std::string& url,
5959
if (allowedUpdates != nullptr) {
6060
auto allowedUpdatesJson = _tgTypeParser.parseArray<std::string>(
6161
[] (const std::string& s)->std::string {
62-
return s;
62+
return "\"" + StringTools::escapeJsonString(s) + "\"";
6363
}, *allowedUpdates);
6464
args.emplace_back("allowed_updates", allowedUpdatesJson);
6565
}

src/TgTypeParser.cpp

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "tgbot/TgTypeParser.h"
22

3+
#include "tgbot/tools/StringTools.h"
4+
35
namespace TgBot {
46

57
Update::Ptr TgTypeParser::parseJsonAndGetUpdate(const boost::property_tree::ptree& data) const {
@@ -97,10 +99,7 @@ std::string TgTypeParser::parseWebhookInfo(const WebhookInfo::Ptr& object) const
9799
appendToJson(result, "last_error_message", object->lastErrorMessage);
98100
appendToJson(result, "last_synchronization_error_date", object->lastSynchronizationErrorDate);
99101
appendToJson(result, "max_connections", object->maxConnections);
100-
appendToJson(result, "allowed_updates", parseArray<std::string>(
101-
[] (const std::string& s)->std::string {
102-
return s;
103-
}, object->allowedUpdates));
102+
appendStringArrayToJson(result, "allowed_updates", object->allowedUpdates);
104103
removeLastComma(result);
105104
result += '}';
106105
return result;
@@ -227,10 +226,7 @@ std::string TgTypeParser::parseChat(const Chat::Ptr& object) const {
227226
appendToJson(result, "last_name", object->lastName);
228227
appendToJson(result, "is_forum", object->isForum);
229228
appendToJson(result, "photo", parseChatPhoto(object->photo));
230-
appendToJson(result, "active_usernames", parseArray<std::string>(
231-
[] (const std::string& s)->std::string {
232-
return s;
233-
}, object->activeUsernames));
229+
appendStringArrayToJson(result, "active_usernames", object->activeUsernames);
234230
appendToJson(result, "birthdate", parseBirthdate(object->birthdate));
235231
appendToJson(result, "business_intro", parseBusinessIntro(object->businessIntro));
236232
appendToJson(result, "business_location", parseBusinessLocation(object->businessLocation));
@@ -1650,10 +1646,7 @@ std::string TgTypeParser::parseGiveaway(const Giveaway::Ptr& object) const {
16501646
appendToJson(result, "only_new_members", object->onlyNewMembers);
16511647
appendToJson(result, "has_public_winners", object->hasPublicWinners);
16521648
appendToJson(result, "prize_description", object->prizeDescription);
1653-
appendToJson(result, "country_codes", parseArray<std::string>(
1654-
[] (const std::string& s)->std::string {
1655-
return s;
1656-
}, object->countryCodes));
1649+
appendStringArrayToJson(result, "country_codes", object->countryCodes);
16571650
appendToJson(result, "premium_subscription_month_count", object->premiumSubscriptionMonthCount);
16581651
removeLastComma(result);
16591652
result += '}';
@@ -3819,15 +3812,9 @@ std::string TgTypeParser::parseInputSticker(const InputSticker::Ptr& object) con
38193812
result += '{';
38203813
appendToJson(result, "sticker", object->sticker);
38213814
appendToJson(result, "format", object->format);
3822-
appendToJson(result, "emoji_list", parseArray<std::string>(
3823-
[] (const std::string& s)->std::string {
3824-
return s;
3825-
}, object->emojiList));
3815+
appendStringArrayToJson(result, "emoji_list", object->emojiList);
38263816
appendToJson(result, "mask_position", parseMaskPosition(object->maskPosition));
3827-
appendToJson(result, "keywords", parseArray<std::string>(
3828-
[] (const std::string& s)->std::string {
3829-
return s;
3830-
}, object->keywords));
3817+
appendStringArrayToJson(result, "keywords", object->keywords);
38313818
removeLastComma(result);
38323819
result += '}';
38333820
return result;
@@ -5416,10 +5403,7 @@ std::string TgTypeParser::parsePassportElementErrorFiles(const PassportElementEr
54165403
// This function will be called by parsePassportElementError(), so I don't add
54175404
// curly brackets to the result std::string.
54185405
std::string result;
5419-
appendToJson(result, "file_hashes",
5420-
parseArray<std::string>([] (const std::string& s)->std::string {
5421-
return s;
5422-
}, object->fileHashes));
5406+
appendStringArrayToJson(result, "file_hashes", object->fileHashes);
54235407
// The last comma will be erased by parsePassportElementError().
54245408
return result;
54255409
}
@@ -5460,10 +5444,7 @@ std::string TgTypeParser::parsePassportElementErrorTranslationFiles(const Passpo
54605444
// This function will be called by parsePassportElementError(), so I don't add
54615445
// curly brackets to the result std::string.
54625446
std::string result;
5463-
appendToJson(result, "file_hashes",
5464-
parseArray<std::string>([] (const std::string& s)->std::string {
5465-
return s;
5466-
}, object->fileHashes));
5447+
appendStringArrayToJson(result, "file_hashes", object->fileHashes);
54675448
// The last comma will be erased by parsePassportElementError().
54685449
return result;
54695450
}
@@ -5582,6 +5563,24 @@ std::string TgTypeParser::parseGenericReply(const GenericReply::Ptr& object) con
55825563
return "";
55835564
}
55845565

5566+
void TgTypeParser::appendStringArrayToJson(std::string& json, const std::string& varName, const std::vector<std::string>& values) const {
5567+
if (values.empty()) {
5568+
return;
5569+
}
5570+
json += '"';
5571+
json += varName;
5572+
json += R"(":[)";
5573+
for (std::size_t i = 0; i < values.size(); ++i) {
5574+
if (i != 0) {
5575+
json += ',';
5576+
}
5577+
json += '"';
5578+
json += StringTools::escapeJsonString(values[i]);
5579+
json += '"';
5580+
}
5581+
json += "],";
5582+
}
5583+
55855584
void TgTypeParser::appendToJson(std::string& json, const std::string& varName, const std::string& value) const {
55865585
if (value.empty()) {
55875586
return;

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
set(TEST_SRC_LIST
22
main.cpp
33
tgbot/Api.cpp
4+
tgbot/TgTypeParser.cpp
45
tgbot/net/Url.cpp
56
tgbot/net/HttpParser.cpp
67
tgbot/tools/StringTools.cpp

test/tgbot/TgTypeParser.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include <memory>
2+
#include <string>
3+
#include <vector>
4+
5+
#include <boost/test/unit_test.hpp>
6+
7+
#include "tgbot/TgTypeParser.h"
8+
#include "tgbot/types/InputSticker.h"
9+
10+
using namespace std;
11+
using namespace TgBot;
12+
13+
BOOST_AUTO_TEST_SUITE(tTgTypeParser)
14+
15+
// Regression tests for https://github.com/reo7sp/tgbot-cpp/issues/346 —
16+
// string arrays were emitted as `[a,b]` (unquoted identifiers) instead of
17+
// `["a","b"]`, producing invalid JSON and breaking sticker creation.
18+
19+
BOOST_AUTO_TEST_CASE(parseInputStickerEmojiListIsJsonArray) {
20+
TgTypeParser parser;
21+
auto sticker = make_shared<InputSticker>();
22+
sticker->sticker = "file_id_abc";
23+
sticker->format = "static";
24+
sticker->emojiList = {"smile", "heart"};
25+
26+
const string json = parser.parseInputSticker(sticker);
27+
28+
BOOST_CHECK(json.find(R"("emoji_list":["smile","heart"])") != string::npos);
29+
// The pre-fix output emitted the array as `[smile,heart]` — assert no regression.
30+
BOOST_CHECK(json.find("[smile,heart]") == string::npos);
31+
}
32+
33+
BOOST_AUTO_TEST_CASE(parseInputStickerEscapesJsonSpecialsInEmoji) {
34+
TgTypeParser parser;
35+
auto sticker = make_shared<InputSticker>();
36+
sticker->sticker = "id";
37+
sticker->format = "static";
38+
sticker->emojiList = {R"(a"b)", R"(c\d)"};
39+
40+
const string json = parser.parseInputSticker(sticker);
41+
42+
BOOST_CHECK(json.find(R"("a\"b")") != string::npos);
43+
BOOST_CHECK(json.find(R"("c\\d")") != string::npos);
44+
}
45+
46+
BOOST_AUTO_TEST_CASE(parseInputStickerOmitsEmptyKeywords) {
47+
// Empty arrays should not appear in the output at all, matching the
48+
// behaviour of the previous `appendToJson(... parseArray(...))` pattern.
49+
TgTypeParser parser;
50+
auto sticker = make_shared<InputSticker>();
51+
sticker->sticker = "id";
52+
sticker->format = "static";
53+
sticker->emojiList = {"a"};
54+
// keywords is default-constructed empty
55+
56+
const string json = parser.parseInputSticker(sticker);
57+
58+
BOOST_CHECK(json.find("keywords") == string::npos);
59+
}
60+
61+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)