Skip to content

Cache targetingData as a JSON string - #58

Open
jrosendahl-opt wants to merge 1 commit into
masterfrom
fix/targeting-cache-json
Open

jrosendahl-opt wants to merge 1 commit into
masterfrom
fix/targeting-cache-json

Conversation

@jrosendahl-opt

@jrosendahl-opt jrosendahl-opt commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Why

targetingFromCache() returns a broken targetingData. LocalStorage.setTargeting() cached the whole OptableTargeting with Gson().toJson(...), and Gson can't serialize org.json.JSONObject: it writes the object's internal fields (nameValuePairs, values). On read, nested objects and arrays come back as strings such as "{values=[{nameValuePairs={...}}]}", and integers become doubles (5001 → 5001.0). Calling getJSONArray("audience") on cached data throws JSONException. gamTargetingKeywords and openRtbJson were unaffected, so GAM and Prebid targeting from the cache still worked.

This has been the case since #35, so v1.0.0–v1.2.0 are affected. It was found while writing a round-trip test for the cache (real SharedPreferences and org.json under Robolectric), which fails on master.

What Changed

  • android_sdk: LocalStorage caches a private CachedTargeting whose targetingData is the JSON string (targetingData.toString()), and rebuilds the JSONObject on read. Its fields carry @SerializedName, so the stored keys stay stable under R8 (Gson's bundled rules keep annotated fields).
  • android_sdk: cache entries in the old format no longer parse, so getTargeting() clears them and returns null, as it already does for unreadable entries.
  • Tests:
    • LocalStorageTest builds its cached entries with setTargeting() instead of Gson().toJson(targeting), which is the old format.
    • It mocks the JSONObject constructor, a stub in local unit tests.
    • New test: an entry in the pre-1.3 format is cleared, not returned. It fails without the fix.

setTargeting() cached the whole OptableTargeting with Gson, which can't
serialize org.json.JSONObject: it wrote the object's internal fields
(nameValuePairs, values). targetingFromCache() then returned a
targetingData whose nested objects and arrays were strings such as
"{values=[...]}" and whose integers were doubles. gamTargetingKeywords
and openRtbJson were unaffected.

Cache a private CachedTargeting with targetingData as its JSON string and
rebuild the JSONObject on read. Entries in the old format no longer parse,
so getTargeting() clears them and returns null, as it already does for
unreadable entries; the next targeting() call refills the cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jrosendahl-opt
jrosendahl-opt requested review from a team as code owners November 3, 2026 21:21
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.

2 participants