Android SQLite вставляет JSON в столбец ТЕКСТ с экранированием двойных кавычек

Я использую SQLite в приложении для Android, в котором, помимо прочего, хранится таблица, в которой записывается настроение пользователя. Схема таблицы показана ниже.

CREATE TABLE moods
(
 dow INTEGER,
 tsn INTEGER,
 lato INTEGER,
 agitation TEXT DEFAULT '{}',
 preoccupancy TEXT DEFAULT '{}',
 tensity TEXT DEFAULT '{}',
 taps TEXT DEFAULT '{}'     
);

В отличие, скажем, от Postgres, SQLite не имеет выделенного типа хранилища jsonb. Цитировать

Backwards compatibility constraints mean that SQLite is only able to store values that are NULL, integers, floating-point numbers, text, and BLOBs. It is not possible to add a sixth "JSON" type.

Документация по расширению SQLite JSON1

В процессоре командной строки SQLite я могу заполнить эту таблицу с помощью простого оператора INSERT, такого как этот

INSERT INTO moods (dow,tsn,lato,agitation,preoccupancy,tensity,taps) 
VALUES(1,0,20,'{"A2":1}','{"P4":2}','{"T4":3}','{"M10":4}');

поскольку он принимает как одинарные, так и двойные кавычки для строк.

Чтобы сделать то же самое в моем гибридном приложении для Android, я делаю следующее

String ag = JOString("A" + DBManage.agitation,1);
String po = JOString("P" + (DBManage.preoccupancy + 15),1);
String te = JOString("T"  + DBManage.tensity,1); 

который возвращает такие строки, как {"P4":2} и т. д. Мой следующий шаг:

ContentValues cv = new ContentValues();
cv.put("dow",0);
cv.put("tsn",1);
cv.put("lato",2);
cv.put("agitation",ag);
cv.put("preoccupancy",po);
cv.put("tensity",te);
long rowid = db.insert("moods",null,cv);

который работает. Однако, изучив сохраненные значения, я обнаружил, что то, что было внесено, на самом деле

{"dow":0,"tsn":5,"lato":191,"tensity":"{\"T0\":1}","agitation":" 
{\"A1\":1}","preoccupancy":"{\"P15\":1}","taps":"{}"}]

Если я не понимаю чего-то здесь, лежащая в основе ContentValues.put или реализация SQLiteDatabase.insert взяли на себя задачу избежать цитируемых строк. Возможно, этого следовало ожидать, учитывая, что преимущество перехода по маршруту SQLiteDatabase.insert с ContentValues заключается в защите от атак SQL-инъекций. Однако в данном случае это помеха, а не помощь.

Если не выполнять необработанный SQL через execSQL, есть ли другой способ поместить JSON в таблицу базы данных SQLite?


Я должен добавить, что просто заменив двойные кавычки одинарными кавычками

String ag = JOString("A" + DBManage.agitation,1);
ag = ag.replaceAll("\"","'");

не сработает, т.к. последующая попытка извлечь JSON из таблицы

SELECT ifnull((select json_extract(preoccupancy,'$.P4') from moods where dow = '1' AND tsn = '0'),0);

приведет к ошибке искаженный JSON, выдаваемой расширением SQLite JSON1.

расширение JSON1 можно включить только с опцией времени сборки SQLITE_ENABLE_JSON1 ... скорее всего, даже недоступно на Android. попробуйте GSON: mvnrepository.com/artifact/com.google.code.gson/gson/2.8.5

Martin Zeitler 11.07.2018 16:23

Эммм ... не знаю, насколько этот комментарий уместен. Если бы мне не удалось использовать SQLite JSON1, я бы задал этот вопрос. 1. JSON1 по умолчанию доступен на Androiid, 2. Я использую версию SQLCipher, в которой он наверняка включен. Я не спрашиваю "почему мой JSON не работает?"

DroidOS 11.07.2018 16:26

можно иметь методы для моделей данных, такие как .fromJson() или .toContentValues() ... также полезен Jackson Object Mapper. json_extract() не всегда возвращает только значения: sqlite.org/json1.html#the_json_extract_function.

Martin Zeitler 11.07.2018 16:31

см. github.com/eugenp/tutorials/tree/master/jackson ... особенно «Пользовательский сериализатор», «Пользовательский десериализатор» (который может быть проще контролировать, чем шаблоны извлечения.

Martin Zeitler 11.07.2018 16:46
1
4
731
0

Другие вопросы по теме