Урок 5. Работа с JSON
Если вы прошли расширенные уроки, то можете смело переходить к разделу...
Работу с JSON будем рассматривать на примере карточки заказа, так как заказ имеет вложенные сущности и потенциально может иметь больше уровней вложенности. Теперь будет интересно увидеть, как изменится форма с использованием JSON-объекта.
Построение объекта
С формы заказа на сервер должен уходить JSON-объект подобного вида:
{
"order_id":3,
"client_id":7,
"order_position":[
{
"order_position_id":1,
"material_id":4,
"quantity":25.00,
"unit_price":200.00,
"updated": true,
"deleted": false
},
{
"order_position_id":null,
"material_id":2,
"quantity":2.00,
"unit_price":350.00,
"updated": false,
"deleted": false
}
]
}
Так как JSON-объект представляет собой набор пар "ключ-значение", то нам необходимо создать структуру <Structure>
типа Dictionary, которую присвоим в объект Variable:
<MyObject Name="OrderDictionaryVariable" Type="Variable" Assembly="SimpleControls" ChangeForm="False">
<Value>
<Structure Type="Dictionary">
<Key Name=""></Key>
</Structure>
</Value>
</MyObject>
Обратите внимание, что у тэга <MyObject>
стоит атрибут ChangeForm
со значением False - этот объект вспомогательный, форма не должна его учитывать при проверке наличия изменений.
Добавим необходимые ключи в структуру и укажем источники данных:
<MyObject Name="OrderDictionaryVariable" Type="Variable" Assembly="SimpleControls" ChangeForm="False">
<Value>
<Structure Type="Dictionary">
<Key Name="order_id">
<DataTypeConvert Type="IntegerDataType">
<Parameter Name="OrderId" Refresh="False" />
</DataTypeConvert>
</Key>
<Key Name="client_id">
<DataConnection SourceDataConnection="OrderPrimaryGetDataConnection" GetScalar="True" Refresh="False">
<Fields>
<Field Name="ClientId" />
</Fields>
</DataConnection>
</Key>
<Key Name="order_position">
<Array>
<Source>
<DataConnection SourceDataConnection="OrderPositionConvertDataConnection" Refresh="False">
<Fields>
<Field Name="OrderPositionId" />
<Field Name="MaterialId" />
<Field Name="Quantity" />
<Field Name="UnitPrice" />
<Field Name="Updated" />
<Field Name="Deleted" />
</Fields>
</DataConnection>
</Source>
<ToDictionary>
<Key Name="order_position_id" Index="0" />
<Key Name="material_id" Index="1" />
<Key Name="quantity" Index="2" />
<Key Name="unit_price" Index="3" />
<Key Name="updated" Index="4" />
<Key Name="deleted" Index="5" />
</ToDictionary>
</Array>
</Key>
</Structure>
</Value>
</MyObject>
Для поля client_id из OrderPrimaryGetDataConnection необходимо получить одно единственное значение - для этого используем атрибут GetScalar
со значением True.
Для поля order_position, содержащего массив объектов, используем конструкцию <Array>
с преобразованием массива строк из ConvertDataConnection в массив словарей.
У тэгов <Parameter>
и <DataConnection>
появился атрибут Refresh
, который определяет, будет ли обновляться значение у тэгов <Key>
и <Array>
, если изменится значение источника. Таким образом, значение объекта OrderDictionaryVariable не будет пересчитываться каждый раз, когда измениться какой-либо источник.
Для ручного пересчета OrderDictionaryVariable нужно использовать команду ValueSetCommand для вызова set-проперти Refresh
у объекта Variable:
<Command Name="OrderDictionaryVariableRefreshValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
<Object Name="OrderDictionaryVariable">
<Property Name="Refresh" />
</Object>
</Command>
Чтобы словарь преобразовать к JSON-объекту, создадим команду типа SerializeToJsonCommand:
<Command Name="OrderDictionaryVariableSerializeToJsonCommand" Type="SerializeToJsonCommand" Assembly="Commands">
<Variable>
<Object Name="OrderDictionaryVariable" />
</Variable>
</Command>
Команда SerializeToJsonCommand при формировании JSON-объекта преобразует все даты со временем к UTC относительно пользовательских настроек временной зоны.
Сохранение изменений
Добавьте кнопку сохранить, как делали на экране со списком ТМЦ.
Создадим SetDataConnection, который в параметре Model будет передавать на сервер сериализованный JSON-объект:
<DataConnection Name="OrderUpdateSetDataConnection" Type="SetDataConnection" Assembly="DataConnections">
<Workflow Name="Template" />
<SqlQueries>
<SqlQuery Name="AppOrderSaveSqlQuery" Type="Update" />
</SqlQueries>
<Parameters>
<Parameter NativeName="Model">
<Value>
<Command Name="OrderDictionaryVariableSerializeToJsonCommand" />
</Value>
</Parameter>
</Parameters>
</DataConnection>
Создадим последовательность команд SaveSequentialCommand, в которую добавим команды на построение JSON_объекта и команду передачи данных на сервер:
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
<Commands>
<Command Name="OrderDictionaryVariableRefreshValueSetCommand" />
<Command Name="OrderDictionaryVariableSerializeToJsonCommand" />
<Command Name="OrderUpdateSaveCommand" />
<Command Name="UpdatedTrueValueSetCommand" />
</Commands>
</Command>
Перейдем в серверный xml-файл и создадим запрос AppOrderSaveSqlQuery:
<SqlQuery Name="AppOrderSaveSqlQuery">
<Text>
SELECT template.order_save_from_app({Model}::json);
</Text>
</SqlQuery>
В запросе вызывается функция на сохранение заказа, в которую передаем json-объект с данными о заказе.
Функция сохранения заказа будет иметь вид:
CREATE OR REPLACE FUNCTION template.order_save_from_app(
in_order json)
RETURNS TABLE(order_id bigint) AS
$BODY$
DECLARE
_order_id bigint = in_order ->> 'order_id';
_client_id bigint = in_order->> 'client_id';
_order_position record;
BEGIN
UPDATE template.order O
SET
client_id = _client_id
WHERE
O.order_id = _order_id;
FOR _order_position IN (
SELECT
*
FROM
json_to_recordset(in_order -> 'order_position') AS T(
order_position_id bigint,
material_id bigint,
quantity numeric,
unit_price numeric
)
)
LOOP
IF (_order_position.order_position_id ISNULL) THEN
INSERT INTO template.order_position (
order_id,
material_id,
quantity,
unit_price
)
VALUES (
_order_id,
_order_position.material_id,
_order_position.quantity,
_order_position.unit_price
);
ELSE
UPDATE template.order_position
SET
material_id = _order_position.material_id,
quantity = _order_position.quantity,
unit_price = _order_position.unit_price
WHERE
order_position_id = _order_position.order_position_id;
END IF;
END LOOP;
RETURN QUERY SELECT _order_id;
END;
$BODY$
LANGUAGE plpgsql;
Сохранение в базу
Реализуйте функцию template.order_save(json). Функция должна возвращать order_id, который необходимо отлавливать на форме TemplateOrderEdit.xml и писать в параметр OrderId.
Не забывайте про работу с датой со временем и временные зоны. В JSON-строке на сервер даты со временем придут в UTC. Для приведения времени во временную зону сервера используйте функцию public.convert_date_json(timestamp without time zone).
Last updated