- 实体属性被重命名
我们假设 sales$Order
实体的 oldNumber
属性被重命名为 newNumber
并且 date
被重命名为 deliveryDate
。在这种情况下,转换配置将如下所示:
<?xml version="1.0"?>
<transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd">
<transformation modelVersion="1.0" currentEntityName="sales$Order">
<renameAttribute oldName="oldNumber" currentName="newNumber"/>
<renameAttribute oldName="date" currentName="deliveryDate"/>
</transformation>
…
</transformations>
如果客户端应用程序需要使用旧版的 sales$Order
实体,那么它必须在 URL 参数中传递 modelVersion
值:
http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93?modelVersion=1.0
将返回以下结果:
{
"_entityName": "sales$Order",
"_instanceName": "00001",
"id": "46322d73-2374-1d65-a5f2-160461da22bf",
"date": "2016-10-31",
"description": "Vacation order",
"oldNumber": "00001"
}
响应 JSON 包含 oldNumber
和 date
属性,尽管 CUBA 应用程序中的实体具有 newNumber
和 deliveryDate
属性。
- 实体名称被更改
接下来,试想一下,在应用程序的某个版本中,sales$Order
实体的名称也发生了变化。新名称是 sales$NewOrder
。
版本 1.1
的转换配置将如下所示:
<?xml version="1.0"?>
<transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd">
<transformation modelVersion="1.1" oldEntityName="sales$Order" currentEntityName="sales$NewOrder">
<renameAttribute oldName="oldNumber" currentName="newNumber"/>
</transformation>
…
</transformations>
除了上一个示例中的配置之外,还在此处添加了 oldEntityName
属性。它指定对模型版本 1.1
有效的实体名称。currentEntityName
属性指定当前实体名称。
虽然名称为 sales$Order
的实体不再存在,但以下请求也可以正常运行:
http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93?modelVersion=1.1
REST API 控制器将认为它必须在 sales$NewOrder
实体里进行搜索,并且在找到具有给定 id 的实体之后,在结果 JSON 中替换实体的名称和 newNumber
属性的名称:
{
"_entityName": "sales$Order",
"_instanceName": "00001",
"id": "46322d73-2374-1d65-a5f2-160461da22bf",
"date": "2016-10-31",
"description": "Vacation order",
"oldNumber": "00001"
}
客户端应用程序也可以使用旧版本的数据模型进行实体更新和创建。
此 POST 请求使用旧实体名称,并且在请求体使用旧的 JSON,这个 POST 请求可以正常工作:
http://localhost:8080/app/rest/v2/entities/sales$Order
{
"_entityName": "sales$Order",
"_instanceName": "00001",
"id": "46322d73-2374-1d65-a5f2-160461da22bf",
"date": "2016-10-31",
"description": "Vacation order",
"oldNumber": "00001"
}
- 必须从 JSON 中删除的实体属性
如果某个属性已添加到实体,但使用旧版数据模型的客户端不希望有此新属性,则可以从结果 JSON 中删除新属性。
此示例的转换配置如下所示:
<?xml version="1.0"?>
<transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd">
<transformation modelVersion="1.5" currentEntityName="sales$Order">
<toVersion>
<removeAttribute name="discount"/>
</toVersion>
</transformation>
…
</transformations>
此配置文件中的转换包含带有嵌套 removeAttribute
命令的 toVersion
标记。这意味着当执行从当前状态到特定版本的转换时(即当请求实体列表时),必须从结果 JSON 中删除 discount
属性。
在这种情况下,如果在没有 modelVersion
属性的情况下执行请求,discount 属性将会被返回:
http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93
{
"_entityName": "sales$Order",
"_instanceName": "00001",
"id": "46322d73-2374-1d65-a5f2-160461da22bf",
"deliveryDate": "2016-10-31",
"description": "Vacation order",
"number": "00001",
"discount": 50
}
如果指定 modelVersion
,则将删除 discount
属性
http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93?modelVersion=1.1
{
"_entityName": "sales$Order",
"_instanceName": "00001",
"id": "46322d73-2374-1d65-a5f2-160461da22bf",
"deliveryDate": "2016-10-31",
"description": "Vacation order",
"oldNumber": "00001"
}
- 使用自定义转换器
还可以创建和注册自定义 JSON 转换器。举个例子,我们来看看下面的情况:有一个实体 sales$OldOrder
被重命名为 sales$NewOrder
。该实体具有 orderDate
字段。在之前的版本中,此日期字段包含时间部分,但在最新版本的实体中,时间部分将被删除。请求旧模型版本 1.0
的实体的 REST API 客户端期望日期字段有时间部分,因此转换器必须修改 JSON 中的值。
首先,转换器配置必须如下所示:
<?xml version="1.0"?>
<transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd">
<transformation modelVersion="1.0" oldEntityName="sales$OldOrder" currentEntityName="sales$NewOrder">
<custom>
<fromVersion transformerBeanRef="sales_OrderJsonTransformerFromVersion"/>
<toVersion transformerBeanRef="sales_OrderJsonTransformerToVersion"/>
</custom>
</transformation>
…
</transformations>
有一个 custom
元素和嵌套的 toVersion
和 fromVersion
元素。这些元素引用了转换器 bean。这意味着自定义转换器必须注册为 Spring bean。这里有一件重要的事情:自定义转换器可以使用 RestTransformations
平台 bean(如果需要,这个 bean 可以访问其它实体转换器)。但是 RestTransformations
bean 在 REST API servlet 的 Spring 上下文中注册,而不是在 Web 应用程序的主上下文中注册。这意味着自定义转换器 bean 也必须在 REST API Spring 上下文中注册。
我们怎样做到这一点?
首先,在 web 或 portal 模块中创建一个 rest-dispatcher-spring.xml
(例如,在 com.company.test
包中)。
接下来,在 Web 或 portal 模块的 app.properties
中注册此文件:
cuba.restSpringContextConfig = +com/company/test/rest-dispatcher-spring.xml
rest-dispatcher-spring.xml
必须包含自定义转换器 bean 定义:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean name="sales_OrderJsonTransformerFromVersion" class="com.company.test.transformer.OrderJsonTransformerFromVersion"/>
<bean name="sales_OrderJsonTransformerToVersion" class="com.company.test.transformer.OrderJsonTransformerToVersion"/>
</beans>
sales_OrderJsonTransformerToVersion
转换器的内容如下:
package com.company.test.transformer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Strings;
import com.haulmont.restapi.transform.AbstractEntityJsonTransformer;
import com.haulmont.restapi.transform.JsonTransformationDirection;
public class OrderJsonTransformerToVersion extends AbstractEntityJsonTransformer {
public OrderJsonTransformerToVersion() {
super("sales$NewOrder", "sales$OldOrder", "1.0", JsonTransformationDirection.TO_VERSION);
}
@Override
protected void doCustomTransformations(ObjectNode rootObjectNode, ObjectMapper objectMapper) {
JsonNode orderDateNode = rootObjectNode.get("orderDate");
if (orderDateNode != null) {
String orderDateNodeValue = orderDateNode.asText();
if (!Strings.isNullOrEmpty(orderDateNodeValue))
rootObjectNode.put("orderDate", orderDateNodeValue + " 00:00:00.000");
}
}
}
此转换器在 JSON 对象中找到 orderDate
节点,并通过将时间部分添加到该值来修改该值。
当请求数据模型版本 1.0
的 sales$OldOrder
实体时,结果 JSON 将包括含有时间部分的 orderDate
字段的实体,尽管它不再存储在数据库中。
关于定制转换器的更多内容:它们必须实现 EntityJsonTransformer
接口。还可以继承 AbstractEntityJsonTransformer
类并覆盖其 doCustomTransformations
方法。AbstractEntityJsonTransformer
类包含标准转换器的所有功能。