fix: [ANDROSDK-2376] store the JSON value of a dataStore entry - #2697
Open
andresmr wants to merge 2 commits into
Open
fix: [ANDROSDK-2376] store the JSON value of a dataStore entry#2697andresmr wants to merge 2 commits into
andresmr wants to merge 2 commits into
Conversation
DataStoreEntryDTO.toDomain called toString() on `value`, a JsonWrapper value
class, so every entry read from the server dataStore was persisted as
"JsonWrapper(json={...})" and could not be deserialized by any consumer.
The same call also turned a null wrapper into the literal string "null", making
an absent value indistinguishable from a JSON null. Serializing the wrapped
JsonElement fixes both.
Both read paths, getNamespaceValues38 and getNamespaceKeyValue, route through
toDomain, so both were affected. Every namespace is, including
ANDROID_SETTINGS_APP, where the bug is masked because settingModule() parses
that namespace into its own typed tables rather than reading the generic
dataStore.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
add specific tests
taridepaco
marked this pull request as ready for review
August 14, 2026 07:27
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



DataStoreEntryDTO.toDomaincalledtoString()onvalue, aJsonWrappervalue class, so every entry read from the server dataStore was persisted asJsonWrapper(json={...})and could not be deserialized by any consumer. The same call turned a null wrapper into the literal string"null", making an absent value indistinguishable from a JSON null. Serializing the wrappedJsonElementand preserving null fixes both.Both read paths —
getNamespaceValues38andgetNamespaceKeyValue— route throughtoDomain, so both were affected, and every namespace is:ANDROID_SETTINGS_APPis stored corrupted too, but the bug is masked there becausesettingModule()parses that namespace into its own typed tables rather than reading the generic dataStore. That is why it went unnoticed since ANDROSDK-2016 — no consumer read the server dataStore raw until now. The new mapping tests fail on 6 of 7 cases against the previous implementation, and the fix was verified end to end against a local DHIS2 instance with the Android Capture App plugin system readingdhis2AndroidPlugins.Migration 181: repairing installations that already downloaded corrupted values
Downloaded entries were stored as
JsonWrapper(json={...})instead of the JSON payload, and an absent value was stored as the literal string"null". Only downloaded rows are affected, so the repair is restricted toSYNCEDentries and leaves anything written locally untouched.The two statements are order-dependent:
UPDATE DataStore SET value = NULL WHERE syncState = 'SYNCED' AND value = 'null';Restores an absent value to
NULL. This must run before the unwrapping below: a genuine JSON null was stored asJsonWrapper(json=null), and unwrapping it first would leave the string"null", which this statement would then wrongly turn into aNULL.UPDATE DataStore SET value = substr(value, 18, length(value) - 18) WHERE syncState = 'SYNCED' AND value LIKE 'JsonWrapper(json=%)';Strips the 17-character
"JsonWrapper(json="prefix and the trailing")"to recover the payload.DataBaseMigrationShould.repair_data_store_values_wrapped_by_json_wrapper_to_string_in_migration_181pins both statements and their ordering against real SQLite: it migrates to 180, seeds a wrapped object, a wrapped array, a genuine JSON null, an absent value and a locally edited row, applies migration 181 and asserts each outcome.Related task: ANDROSDK-2376