Skip to content

Fix JSON string-array serialization in TgTypeParser (closes #346)#354

Open
Dramex wants to merge 1 commit intoreo7sp:masterfrom
Dramex:fix/346-json-string-array-serialization
Open

Fix JSON string-array serialization in TgTypeParser (closes #346)#354
Dramex wants to merge 1 commit intoreo7sp:masterfrom
Dramex:fix/346-json-string-array-serialization

Conversation

@Dramex
Copy link
Copy Markdown

@Dramex Dramex commented Apr 18, 2026

Closes #346.

The bug

Seven call sites passed parseArray<std::string> an identity lambda:

appendToJson(result, "emoji_list", parseArray<std::string>(
    [] (const std::string& s) -> std::string { return s; },  // <- identity
    object->emojiList));

The identity lambda produces [smile,heart] (unquoted identifiers) instead of ["smile","heart"]. That's not valid JSON, and @BloodRedTape confirmed in the issue thread that sticker creation is broken because of it. The same pattern exists in Api::setWebhook for allowed_updates.

The fix

Added a private helper in TgTypeParser:

void TgTypeParser::appendStringArrayToJson(std::string& json,
                                           const std::string& varName,
                                           const std::vector<std::string>& values) const;

which emits "name":["escaped","values"], directly, running each element through StringTools::escapeJsonString. It's a no-op when the vector is empty, matching the prior appendToJson(... parseArray(...)) behaviour.

Replaced the seven broken call sites in TgTypeParser.cpp:

  • WebhookInfo.allowed_updates
  • Chat.active_usernames
  • Giveaway.country_codes
  • InputSticker.emoji_list (the issue's primary reproducer)
  • InputSticker.keywords
  • PassportElementErrorFiles.file_hashes
  • PassportElementErrorTranslationFiles.file_hashes

And fixed the single analogous site in Api::setWebhook by aligning its lambda with the pattern the other Api.cpp string-array sites already use (return "\"" + StringTools::escapeJsonString(s) + "\"").

What I did NOT change

I initially experimented with widening appendToJson(std::string)'s bracket heuristic to recognise [...] as pre-serialized JSON — this would have also fixed latent bugs in other array-wrapping call sites (e.g. parseMessage's entities). I reverted that because it would regress any field whose value legitimately starts with [ and ends with ] (keyboard-button text like "[CLICK]", captions, etc.). A dedicated helper is safer.

Only paths actually used by real Bot API requests are affected by this fix (setWebhook, createNewStickerSet/addStickerToSet via parseInputSticker, answerInlineQuery via the passport-error serializers).

Testing

Added test/tgbot/TgTypeParser.cpp with three regression tests:

  • parseInputStickerEmojiListIsJsonArray — asserts the output contains "emoji_list":["smile","heart"] and not the pre-fix [smile,heart].
  • parseInputStickerEscapesJsonSpecialsInEmoji — asserts inner " and \ get properly JSON-escaped.
  • parseInputStickerOmitsEmptyKeywords — preserves prior behaviour where empty arrays are omitted from the output entirely.

Local build (Boost 1.90, OpenSSL 3.6, clang 17 on macOS arm64):

$ ./test/TgBot_test
Running 18 test cases...
*** No errors detected

(15 pre-existing + 3 new, all passing.)

Also verified end-to-end with a small smoke test:

Before: {"sticker":"file_id","format":"static","emoji_list":"[smile,heart]"}
After:  {"sticker":"file_id","format":"static","emoji_list":["smile","heart"]}

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 reo7sp#346
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bugs recognized in TgTypeParser

1 participant