一个最简单的kite使用示例:
KiteOptions options = new KiteOptions();
KiteFactory kiteFactory = KiteFactoryBuilder.buildFromClasspathXml(options,"/kite/kite-demo.xml");
ObjectMapper objectMapper = new ObjectMapper();
kiteFactory.useJsonFramework(new JacksonFramework(objectMapper));
kiteFactory.useXmlFramework(new Dom4jFramework());
DataModel dataModel = DataModel.singleton("sayHello","Hello Kite!");
// 生成json
String json = kiteFactory.getJsonProducer(dataModel,"kite-demo","first-view").produce(false);
System.out.println(json);
// 生成xml
String xml = kiteFactory.getXmlProducer(dataModel,"kite-demo","first-view").produce(false);
System.out.println(xml);
你需要一份Kite XML配置,位置在上述声明的/kite/kite-demo.xml:
<kite-configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://github.com/developframework/kite/schema"
xsi:schemaLocation="
https://github.com/developframework/kite/schema kite-configuration.xsd">
<template-package namespace="kite-demo">
<template id="first-view">
<property data="sayHello"/>
</template>
</template-package>
</kite-configuration>
运行结果:
{"sayHello":"Hello Kite!"}
<xml>
<say-hello>Hello Kite!</say-hello>
</xml>
com.github.developframework.kite.data.DataModel
Kite框架的数据模型。用于装载需要在视图中渲染的数据或函数接口实现,数据由键值对构成。接口提供存入和取出数据的方法,支持链式写法。
DataModel dataModel = DataModel.singleton("sayHello","Hello Kite!");
com.github.developframework.expression.Expression
是kite框架从DataModel中提取数据的表达式。不论dataModel存的是java实体类还是Map对象都可以使用表达式取值。 范例:
student
你可以从DataModel对象内取得名为student的对象#student
你可以从DataModel对象内 强制从根路径 取得名为student的对象student.name
你可以从DataModel对象内取得名为student的对象的name属性值students[0]
你可以从DataModel对象内取得名为students的数组内的第1个元素student[0].name
你可以从DataModel对象内取得名为students的数组内的第1个元素的name属性值
Expression
的详细使用请查看独立项目expression
com.github.developframework.kite.core.KiteOptions
Kite框架的配置类。
KiteOptions options=new KiteOptions();
options.getJson().setNamingStrategy(NamingStrategy.LOWER_CASE);
com.github.developframework.kite.core.KiteFactory
类是Kite框架的构建工厂。使用Kite框架的第一步就是建立该对象。 建立该对象需要提供配置文件路径的字符串,多份配置文件可以采用字符串数组。
final String[]xmlFile s= {"config1.xml","config2.xml"};
KiteFactory kiteFactory = KiteFactoryBuilder.buildFromClasspathXml(options,xmlFiles);
kite-core
是一套接口,需要加入实现包才能运行,目前有以下实现包:
kite-jackson
使用jackson来序列化jsonkite-dom4j
使用dom4j来序列化xmlkite-fastjson
使用fastjson来序列化jsonkite-gson
使用gson来序列化json
kiteFactory.useJsonFramework(new JacksonFramework(objectMapper));
kiteFactory.useXmlFramework(new Dom4jFramework());
kiteFactory.useJsonFramework(new FastjsonFramework());
kiteFactory.useJsonFramework(new GsonFramework());
com.github.developframework.kite.core.Producer
接口是json字符串建造类,根据kiteFactory.useFramework()
方法传入的Framework
对象不同可以得出不同实现的Producer。
// 直接生成字符串结果
String produce(boolean pretty);
// 向输出流输出结果
void output(OutputStream outputStream,Charset charset,boolean pretty);
Kite框架的所有异常类。
异常 | 说明 |
---|---|
KiteException | kite顶级异常 |
ConfigurationSourceException | 配置源异常 |
TemplateException | template异常 |
TemplatePackageUndefinedException | templatePackage未定义异常 |
KiteParseXmlException | 配置文件解析错误异常 |
LinkSizeNotEqualException | 使用link功能时数组大小不相等异常 |
ResourceNotUniqueException | 资源定义不唯一异常 |
InvalidArgumentsException | 无效的参数异常 |
Kite configuration 文档的结构如下:
<kite-configuration>
<template-package namespace="">
<template id="">
<!-- 定义视图内容 -->
</template>
<fragment id="">
<!-- 定义片段内容 -->
</fragment>
<!-- 其它template -->
</template-package>
<!-- 其它template-package -->
</kite-configuration>
在<kite-configuration>
节点中你可以配置任意数量的<template-package>
,代表不同的模板包,在<template-package>
节点上你必须声明命名空间namespace
属性,并且namespace
是唯一的,不然会抛出ResourceNotUniqueException
。
在每个<template-package>
节点中你可以配置任意数量的<template>
。每个<template>
即代表了某一种json格式的视图,在<template>
节点你必须声明id属性,并且id必须是唯一的,不然会抛出ResourceNotUniqueException
。
Kite configuration文档不是唯一的,Kite框架允许你拥有多份的Kite configuration配置文档,文档的加载顺序不分先后。
基本型标签
<template>
和<fragment>
<object>
<array>
<property>
<this>
<prototype>
raw
<xml-attribute>
功能型标签
<include>
<link>
<relevance>
<object-virtual>
<flat>
<slot>
<if>
、<else>
<switch>
、<case>
、<default>
拓展型标签
-
<property-date>
-
<property-unixtimestamp>
-
<property-boolean>
-
<property-enum>
enum
当你需要声明一个片段时,你将会使用到<fragment>
标签
当你需要声明一个模板时,你将会使用到<template>
标签
<fragment id="">
</fragment>
<template id="">
</template>
属性 | 功能 | 是否必须 |
---|---|---|
id | 声明模板编号,在命名空间中唯一 | 是 |
data | 取值表达式 | 否 |
extend | 声明继承的kite和端口,格式为namespace.id(namespace不填时默认为当前template所在的namespace) | 否 |
xml-root | 生成xml时的根节点名称 | 否 |
template包含<array>
节点的所有属性
template和fragment的区别:
- template是fragment的子类,它也是一个片段
- fragment不能被直接调用,只能include或被extend,而template可以直接被调用
当你需要构建一个对象结构时,你将会使用到<object>
标签
<object data="">
</object>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
converter | 类型转换器全限定类名或expression表达式。 | 否 |
当你需要构建一个数组结构时,你将会使用到<array>
标签
<array data="">
</array>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
converter | 类型转换器全限定类名或expression表达式 | 否 |
map | MapFunction的实现类全名或Expression表达式 | 否 |
xml-item | 生成xml时,子节点数组项的节点名称 | 否 |
limit | 取前若干个元素 | 否 |
comparator | Comparator比较器接口实现类表达式 | 否 |
null-empty | true时表示表达式取的值为null时设为空数组,默认为false | 否 |
<array>
标签可以没有子标签,这时表示数组为基本类型数组,如果是对象将会调用它的toString()
方法。
当你需要构建一个普通属性结构时, 你将会使用到<property>
标签。
<property data=""/>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
converter | 类型转换器全限定类名或expression表达式 | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
xml-cdata | 生成xml时是否使用<![CDATA[ ]]> ,默认false |
否 |
指代节点本身值
<this alias="">
</this>
属性 | 功能 | 是否必须 |
---|---|---|
alias | 别名,你可以重新定义显示名 | 是 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
converter | 类型转换器全限定类名或expression表达式 | 否 |
xml-cdata | 生成xml时是否使用<![CDATA[ ]]> ,默认false |
否 |
原型实体构建结构,即使用实现框架自己的序列化功能, 你将会使用到<prototype>
标签
<prototype data=""/>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
converter | 类型转换器全限定类名或expression表达式 | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
使用raw字符串构建结构, 你将会使用到<raw>
标签。
<raw data=""/>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
converter | 类型转换器全限定类名或expression表达式 | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
在输出xml时,提供配置xml节点的属性值。
<xml-attribute data=""/>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
Kite框架提供模块化设计json结构视图的功能。在一个<template>
标签中你可以采用<include>
标签来导入其它的<template>
的结构内容,从而实现模块化单元分解
<include id="" namespace=""/>
属性 | 功能 | 是否必须 |
---|---|---|
id | 需要导入的template id | 是 |
namespace | template的命名空间,不填默认为当前命名空间 | 否 |
该标签用于实现一对一链接对象功能
<link data="">
</link>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式,data必须指代一个List或array类型的对象 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
该标签用于实现一对多关联功能。
<relevance data="" rel="">
</relevance>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式,data必须指代一个List或array类型的对象 | 是 |
alias | 别名,你可以重新定义显示名 | 否 |
naming-strategy | 命名策略,默认FRAMEWORK | 否 |
rel | 关联判定器全限定类名或expression表达式 | 是 |
null-hidden | true时表示表达式取的值为null时隐藏该节点,默认为false | 否 |
map | KiteConverter的实现类全名或Expression表达式。 | 否 |
null-empty | true时表示表达式取的值为null时设为空数组,默认为false | 否 |
merge-parent | 单个值时可以拼接到父级 | 否 |
该标签用于虚拟对象结构
<object-virtual alias="">
</object-virtual>
属性 | 功能 | 是否必须 |
---|---|---|
alias | 别名 | 是 |
该标签用于扁平化对象结构,把子层节点拼接到父层
<flat data="">
</flat>
属性 | 功能 | 是否必须 |
---|---|---|
data | 取值表达式 | 是 |
converter | 类型转换器全限定类名或expression表达式 | 否 |
此标签位置作为子<template>
的插槽位置。和template的extend
结合使用
<template id="" extend="">
<slot/>
</template>
分支结构标签
<if condition="">
</if>
<else>
</else>
属性 | 功能 | 是否必须 |
---|---|---|
condition | 条件接口实现类全名或Expression表达式 | 是 |
分支结构标签
<switch check-data="">
<case test=""></case>
<case test=""></case>
<default></default>
</switch>
属性 | 功能 | 是否必须 |
---|---|---|
check-data | 取值表达式 | 否 |
test | CaseTestFunction接口实现类表达式 | 是 |
该标签拓展于<property>
,针对时间日期类型,使用pattern
属性来格式化日期时间
拓展属性 | 功能 | 是否必须 |
---|---|---|
pattern | 格式化模板字符串,不填默认为"yyyy-MM-dd HH:mm:ss" | 否 |
支持的时间日期类型:
- java.util.Date
- java.sql.Date
- java.sql.Time
- java.sql.Timestamp
- (JDK8) java.time.LocalDate
- (JDK8) java.time.LocalDateTime
- (JDK8) java.time.LocalTime
- (JDK8) java.time.Instant
该标签拓展于<property>
,针对时间日期类型,可以将时间日期类型转化为unix时间戳格式显示。可支持的时间日期类型同<property-date>
。
该标签拓展于<property>
,可以将数字类型(short、int、long)变为boolean型,非0值为true,0值为false。
该标签拓展于<property>
,可以将值映射成另一个固定值。
模型声明(以下各小节示例代码均使用这些模型实体类):
// 公司类 Company.java
@Data
@RequiredArgsConstructor
public class Company {
// 公司ID
private final Integer companyId;
// 公司名称
private final String companyName;
}
// 部门 Department.java 一个公司多个部门
@Data
@RequiredArgsConstructor
public class Department {
// 部门ID
private final Integer departmentId;
// 部门名称
private final String departmentName;
}
// 员工 Staff.java 一个部门多个
@Data
@RequiredArgsConstructor
public class Staff {
// 员工ID
private final Integer staffId;
// 部门ID
private final Integer departmentId;
// 员工姓名
private final String staffName;
// 性别
private final Gender gender;
// 生日
private final LocalDate birthday;
public enum Gender {
MALE, FEMALE, UNKNOWN
}
}
对应的kite configuration文件配置
<!-- 忽略kite-configuration -->
<template-package namespace="kite-demo">
<template id="company-info" data="company">
<property data="companyId"/>
<property data="companyName"/>
</template>
<template id="department-info" data="department">
<property data="departmentId"/>
<property data="companyId"/>
<property data="departmentName"/>
</template>
<template id="staff-info" data="staff">
<property data="staffId"/>
<property data="departmentId"/>
<property data="staffName"/>
<property data="gender"/>
<property-date data="birthday"/>
</template>
</template-package>
伪造数据
public final class DemoDataMock {
public static Company[] mockCompanies() {
return new Company[]{
new Company(1, "XX科技公司"),
new Company(2, "YY网络公司")
};
}
public static Department[] mockDepartments() {
return new Department[]{
new Department(11, 1, "技术部"),
new Department(12, 2, "运营部"),
new Department(13, 1, "财务部"),
new Department(14, 2, "事业部")
};
}
public static Staff[] mockStaffs() {
return new Staff[]{
new Staff(111, 11, "小张", Staff.Gender.MALE, LocalDate.of(1988, 2, 3)),
new Staff(112, 12, "小王", Staff.Gender.FEMALE, LocalDate.of(1992, 12, 6)),
new Staff(113, 13, "小李", Staff.Gender.FEMALE, LocalDate.of(1995, 6, 15)),
new Staff(114, 14, "小孙", Staff.Gender.UNKNOWN, LocalDate.of(1983, 11, 8)),
new Staff(115, 12, "小郑", Staff.Gender.FEMALE, LocalDate.of(1986, 9, 5)),
new Staff(116, 13, "小钱", Staff.Gender.MALE, LocalDate.of(1984, 10, 1)),
new Staff(117, 14, "小潘", Staff.Gender.FEMALE, LocalDate.of(1990, 12, 12)),
};
}
public static Integer[] mockScores() {
return new Integer[]{70, 85, 93, 87, 81, 94, 97, 86};
}
}
// 初始化kiteFactory 后续不再赘述
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
final JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
objectMapper.registerModules(javaTimeModule);
final KiteFactory kiteFactory = KiteFactoryBuilder.buildFromClasspathXml(new KiteOptions(),"kite/kite-demo.xml");
kiteFactory.useJsonFramework(new JacksonFramework(objectMapper));
kiteFactory.useXmlFramework(new Dom4jFramework());
DataModel dataModel = DataModel.singleton("company",DemoDataMock.mockCompanies()[0]);
String json = kiteFactory.getJsonProducer(dataModel,"kite-demo","company-info").produce(false);
String xml = kiteFactory.getXmlProducer(dataModel,"kite-demo","company-info").produce(false);
System.out.println(json);
System.out.println(xml);
执行结果:
{
"company_id": 1,
"company_name": "XX科技公司"
}
<xml>
<company-id>1</company-id>
<company-name>XX科技公司</company-name>
</xml>
<template id="company-info" data="company">
<property data="companyId" alias="id"/>
<property data="companyName" alias="name"/>
</template>
{
"id": 1,
"name": "XX科技公司"
}
<xml>
<id>1</id>
<name>XX科技公司</name>
</xml>
com.github.developframework.kite.core.strategy.KitePropertyNamingStrategy
json节点和xml节点名称的命名策略扩展,继承接口
public interface KitePropertyNamingStrategy {
/**
属性最终显示名称
*/
String propertyDisplayName(Framework<?> framework, String name);
}
传入的name是data的变量名称,通过接口方法改写得出最终要显示的名称。
Kite内置接口实现:
- FRAMEWORK JacksonKitePropertyNamingStrategy 用实现框架配置的策略命名,比如Jackson ObjectMapper设置的PropertyNamingStrategy
- MIDDLE_LINE MiddleLineKitePropertyNamingStrategy 中划线命名策略,AbCd => ab-cd
- UNDERLINE UnderlineXmlKitePropertyNamingStrategy 下划线xml命名策略,AbCd => ab_cd
- LOWDER_CASE LowerCaseKitePropertyNamingStrategy 全小写命名策略, AbCd => abcd
- ORIGINAL OriginalKitePropertyNamingStrategy 什么都不做,使用原名
可在KiteOptions
里分别给json和xml的序列化设置全局命名策略
final KiteOptions options=new KiteOptions();
options.getJson().setNamingStrategy(NamingStrategy.UNDERLINE);
options.getXml().setNamingStrategy(NamingStrategy.MIDDLE_LINE);
也可以在实际的模板里某个内容节点上配置naming-strategy
属性强制使用某个策略
<template id="test-naming-strategy">
<object data="myCompany" naming-strategy="ORIGINAL">
<property data="companyId" naming-strategy="MIDDLE_LINE"/>
<property data="companyName" naming-strategy="UNDERLINE"/>
</object>
</template>
{
"myCompany": {
"company-id": 1,
"company_name": "XX科技公司"
}
}
<xml>
<myCompany>
<company-id>1</company-id>
<company_name>XX科技公司</company_name>
</myCompany>
</xml>
该标签可以格式化时间
<property data="birthday"/>
替换为
<property-date data="birthday" pattern="yyyy年MM月dd日"/>
运行结果:
{
"birthday": "1995年01月01日"
}
该标签可以使日期时间类型转化成时间戳形式输出。
dataModel.putData("datetime", LocalDateTime.of(2016, 1, 1, 0, 0, 0));
{"datetime" : 1451577600}
该标签可以把非0数字转换成true,0转换成false
DataModel dataModel = new DataModel();
dataModel.putData("number1", 1);
dataModel.putData("number2", 0);
{"number1" : true, "number2" : false}
该标签可以把值映射成另一个固定值,该标签不仅可以处理枚举类型,字符串或者基本类型都可以处理
<property-enum data="gender">
<enum value="MALE" text="男"/>
<enum value="FEMALE" text="女"/>
</property-enum>
DataModel dataModel=DataModel.singleton("gender",Staff.Gender.MALE);
{
"gender": "男"
}
4.5. null-hidden和null-empty
所有的内容节点可以用null-hidden
来隐藏data指代对象值为null的节点
<array>
标签可以用null-empty
属性把data指代对象值为null的节点设为空数组
<template id="test-array-null">
<array data="array" alias="null-array"/>
<array data="array" alias="empty-array" null-empty="true"/>
<array data="array" alias="null-hidden-array" null-hidden="true"/>
</template>
DataModel dataModel=DataModel.singleton("array",null);
{
"null-array": null,
"empty-array": []
}
<xml>
<null-array/>
<empty-array/>
</xml>
<template id="company-info">
<object data="company">
<property data="companyId"/>
<property data="companyName"/>
</object>
</template>
利用<object>
标签构造一个对象结构
DataModel dataModel=DataModel.singleton("company",DemoDataMock.mockCompanies()[0]);
{
"company": {
"company_id": 1,
"company_name": "XX科技公司"
}
}
<xml>
<company>
<company-id>1</company-id>
<company-name>XX科技公司</company-name>
</company>
</xml>
利用array
标签构造一个数组结构:
<template id="company-info">
<array data="companies" xml-item="company">
<property data="companyId"/>
<property data="companyName"/>
</array>
</template>
DataModel dataModel=DataModel.singleton("company",DemoDataMock.mockCompanies());
{
"companies": [
{
"company_id": 1,
"company_name": "XX科技公司"
},
{
"company_id": 2,
"company_name": "YY网络公司"
}
]
}
<xml>
<companies>
<company>
<company-id>1</company-id>
<company-name>XX科技公司</company-name>
</company>
<company>
<company-id>2</company-id>
<company-name>YY网络公司</company-name>
</company>
</companies>
</xml>
或者直接把data设定在<template>
标签上,Kite框架会自动识别data对应的数据是否是数组或List。
<template id="company-info" data="company" xml-item="company" xml-root="companies">
<property data="companyId"/>
<property data="companyName"/>
</template>
当data指代的对象是Array或Collection时生成数组结构
[
{
"company_id": 1,
"company_name": "XX科技公司"
},
{
"company_id": 2,
"company_name": "YY网络公司"
}
]
<companies>
<company>
<company-id>1</company-id>
<company-name>XX科技公司</company-name>
</company>
<company>
<company-id>2</company-id>
<company-name>YY网络公司</company-name>
</company>
</companies>
当data指代的对象是单个对象时生成对象结构
{
"company_id": 1,
"company_name": "XX科技公司"
}
<companies>
<company-id>1</company-id>
<company-name>XX科技公司</company-name>
</companies>
使用<prototype>
标签可以使用原生的框架转换成json,以下展示使用Jackson作为实现框架
<template id="staff-info">
<prototype data="staff"/>
</template>
以下加入Jackson的注解达到序列化效果
@Data
@RequiredArgsConstructor
public class Staff {
// 员工ID
private final Integer staffId;
// 部门ID 忽略该属性
@JsonIgnore
private final Integer departmentId;
// 员工姓名 改写属性名
@JsonProperty("name")
private final String staffName;
// 性别
private final Gender gender;
// 生日
private final LocalDate birthday;
public enum Gender {
MALE, FEMALE, UNKNOWN
}
}
{
"staff": {
"staff_id": 111,
"gender": "MALE",
"birthday": "1988-02-03",
"name": "小张"
}
}
更多注解使用请参考jackson-annotations文档。
使用<raw>
来添加原文本对象,内部使用具体实现框架来反序列化的。
<template id="test-raw">
<raw data="companyJson" alias="company"/>
</template>
String rawJson = "{\"company_id\":1,\"company_name\":\"XX科技公司\"}";
DataModel dataModel = DataModel.singleton("companyJson", rawJson);
{
"company": {
"company_id": 1,
"company_name": "XX科技公司"
}
}
Kite框架使用slf4j-api日志接口,提供内部日志打印功能。可以使用log4j或者logback打印日志。 以下示例使用logback
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>kite-log</contextName>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.github.developframework.kite" level="INFO" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
</configuration>
项目启动日志:
09:29:07.753 【Kite】已加载配置源“/kite/kite-demo.xml”