443 lines
14 KiB
Markdown
443 lines
14 KiB
Markdown
# 序列化
|
||
  Convert提供Java对象的序列化与反序列化功能。支持JSON(JavaScript Object Notation)、PROTOBUF两种格式化。 两种格式使用方式完全一样,其性能都大幅度超过其他JSON框架。同时JSON内置于HTTP服务中,PROTOBUF也是SNCP协议数据序列化的基础。
|
||
## 基本API
|
||
  JSON序列化其操作类主要是JsonConvert,配置类主要是JsonFactory、ConvertColumn。JsonFactory采用同ClassLoader类似的双亲委托方式设计。
|
||
JsonConvert 序列化encode方法:
|
||
```java
|
||
public String convertTo(Object value);
|
||
|
||
public String convertTo(Type type, Object value);
|
||
|
||
public void convertTo(OutputStream out, Object value);
|
||
|
||
public void convertTo(OutputStream out, Type type, Object value);
|
||
|
||
public ByteBuffer[] convertTo(Supplier<ByteBuffer> supplier, Object value);
|
||
|
||
public ByteBuffer[] convertTo(Supplier<ByteBuffer> supplier, Type type, Object value);
|
||
|
||
public void convertTo(JsonWriter writer, Object value);
|
||
|
||
public void convertTo(JsonWriter writer, Type type, Object value);
|
||
```
|
||
JsonConvert 反序列化decode方法:
|
||
```java
|
||
public <T> T convertFrom(Type type, String text);
|
||
|
||
public <T> T convertFrom(Type type, char[] text);
|
||
|
||
public <T> T convertFrom(Type type, char[] text, int start, int len);
|
||
|
||
public <T> T convertFrom(Type type, InputStream in);
|
||
|
||
public <T> T convertFrom(Type type, ByteBuffer... buffers);
|
||
|
||
public <T> T convertFrom(Type type, JsonReader reader);
|
||
```
|
||
## JSON基本用法
|
||
```java
|
||
@Data
|
||
public class UserRecord {
|
||
|
||
private int userid;
|
||
|
||
private String username = "";
|
||
|
||
private String password = "";
|
||
|
||
public UserRecord() {
|
||
}
|
||
}
|
||
|
||
public static void main(String[] args) throws Exception {
|
||
UserRecord user = new UserRecord();
|
||
user.setUserid(100);
|
||
user.setUsername("redkalename");
|
||
user.setPassword("123456");
|
||
final JsonConvert convert = JsonConvert.root();
|
||
String json = convert.convertTo(user);
|
||
System.out.println(json); //应该是 {"password":"123456","userid":100,"username":"redkalename"}
|
||
UserRecord user2 = convert.convertFrom(UserRecord.class, json);
|
||
//应该也是 {"password":"123456","userid":100,"username":"redkalename"}
|
||
System.out.println(convert.convertTo(user2));
|
||
|
||
/**
|
||
* 以下功能是为了屏蔽password字段。
|
||
* 等价于 public String getPassword() 加上 @ConvertColumn :
|
||
*
|
||
* @ConvertColumn(ignore = true, type = ConvertType.JSON)
|
||
* public String getPassword() {
|
||
* return password;
|
||
* }
|
||
**/
|
||
final JsonFactory childFactory = JsonFactory.root().createChild();
|
||
childFactory.register(UserRecord.class, true, "password"); //屏蔽掉password字段使其不输出
|
||
childFactory.reloadCoder(UserRecord.class); //重新加载Coder使之覆盖父Factory的配置
|
||
final JsonConvert childConvert = childFactory.getConvert();
|
||
json = childConvert.convertTo(user);
|
||
System.out.println(json); //应该是 {"userid":100,"username":"redkalename"}
|
||
user2 = childConvert.convertFrom(UserRecord.class, json);
|
||
//应该也是 {"userid":100,"username":"redkalename"}
|
||
System.out.println(childConvert.convertTo(user2));
|
||
}
|
||
```
|
||
  在Redkale里存在默认的JsonConvert、ProtobufConvert对象。 只需在所有Service、Servlet中增加依赖注入资源。
|
||
```java
|
||
public class XXXService implements Service {
|
||
|
||
@Resource
|
||
private JsonConvert jsonConvert;
|
||
|
||
@Resource
|
||
private ProtobufConvert protobufConvert;
|
||
}
|
||
|
||
public class XXXServlet extends HttpServlet {
|
||
|
||
@Resource
|
||
private JsonConvert jsonConvert;
|
||
|
||
@Resource
|
||
private ProtobufConvert protobufConvert;
|
||
|
||
}
|
||
```
|
||
   同一类型数据通过设置新的JsonFactory可以有不同的输出。
|
||
```java
|
||
@Data
|
||
public class UserSimpleInfo {
|
||
|
||
private int userid;
|
||
|
||
private String username = "";
|
||
|
||
@ConvertColumn(ignore = true, type = ConvertType.JSON)
|
||
private long regtime; //注册时间
|
||
|
||
@ConvertColumn(ignore = true, type = ConvertType.JSON)
|
||
private String regaddr = ""; //注册IP
|
||
|
||
}
|
||
|
||
public class UserInfoServlet extends HttpBaseServlet {
|
||
|
||
@Resource
|
||
private UserSerice service;
|
||
|
||
@Resource
|
||
private JsonFactory jsonRootFactory;
|
||
|
||
@Resource
|
||
private JsonConvert detailConvert;
|
||
|
||
@Override
|
||
public void init(HttpContext context, AnyValue config) {
|
||
final JsonFactory childFactory = jsonRootFactory.createChild();
|
||
childFactory.register(UserSimpleInfo.class, false, "regtime", "regaddr"); //允许输出注册时间与注册地址
|
||
childFactory.reloadCoder(UserSimpleInfo.class); //重新加载Coder使之覆盖父Factory的配置
|
||
this.detailConvert = childFactory.getConvert();
|
||
}
|
||
|
||
// 获取他人基本信息
|
||
@HttpMapping(url = "/user/info/")
|
||
public void info(HttpRequest req, HttpResponse resp) throws IOException {
|
||
int userid = Integer.parseInt(req.getRequstURILastPath());
|
||
UserSimpleInfo user = service.findUserInfo(userid);
|
||
resp.finishJson(user); // 不包含用户的注册时间和注册地址字段信息
|
||
}
|
||
|
||
//获取用户自己的信息
|
||
@HttpMapping(url = "/user/myinfo")
|
||
public void mydetail(HttpRequest req, HttpResponse resp) throws IOException {
|
||
int userid = req.currentUser().getUserid(); //获取当前用户ID
|
||
UserSimpleInfo user = service.findUserInfo(userid);
|
||
resp.finishJson(detailConvert, user); // 包含用户的注册时间和注册地址字段信息
|
||
}
|
||
}
|
||
```
|
||
|
||
  Convert 支持带参数构造函数。
|
||
|
||
    1. public 带参数的构造函数加上 @ConstructorParameters 注解:
|
||
```java
|
||
public class UserRecord {
|
||
|
||
private int userid;
|
||
|
||
private String username = "";
|
||
|
||
private String password = "";
|
||
|
||
@ConstructorParameters({"userid", "username", "password"})
|
||
public UserRecord(int userid, String username, String password) {
|
||
this.userid = userid;
|
||
this.username = username;
|
||
this.password = password;
|
||
}
|
||
|
||
public int getUserid() {
|
||
return userid;
|
||
}
|
||
|
||
public String getUsername() {
|
||
return username;
|
||
}
|
||
|
||
public String getPassword() {
|
||
return password;
|
||
}
|
||
}
|
||
```
|
||
    2. 自定义Creator:
|
||
```java
|
||
public class UserRecord {
|
||
|
||
private int userid;
|
||
|
||
private String username = "";
|
||
|
||
private String password = "";
|
||
|
||
UserRecord(int userid, String username, String password) {
|
||
this.userid = userid;
|
||
this.username = username;
|
||
this.password = password;
|
||
}
|
||
|
||
public int getUserid() {
|
||
return userid;
|
||
}
|
||
|
||
public String getUsername() {
|
||
return username;
|
||
}
|
||
|
||
public String getPassword() {
|
||
return password;
|
||
}
|
||
|
||
/**
|
||
* 自定义Creator方法。
|
||
* 1) 方法名可以随意。
|
||
* 2) 方法必须是static。
|
||
* 3)方法的参数必须为空。
|
||
* 4)方法的返回类型必须是Creator。
|
||
*
|
||
* @return
|
||
*/
|
||
private static Creator<UserRecord> creator() {
|
||
return new Creator<UserRecord>() {
|
||
@Override
|
||
@ConstructorParameters({"userid", "username", "password"}) //带参数的构造函数必须有ConstructorParameters注解
|
||
public UserRecord create(Object... params) {
|
||
int userid = (params[0] == null ? 0 : (Integer) params[0]);
|
||
return new UserRecord(userid, (String) params[1], (String) params[2]);
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
```
|
||
  通常JavaBean类默认有个public空参数的构造函数,因此大部分情况下不要自定义Creator,其实只要不是private的空参数构造函数Convert都能自动支持(其他的框架都仅支持public的构造函数),只有仅含private的构造函数才需要自定义Creator。带参数的构造函数就需要标记@ConstructorParameters,常见于Immutable Object。
|
||
|
||
## 自定义
|
||
  Convert 支持自定义Decode、Encode。
|
||
|
||
    1. 通过ConvertFactory显式的注册:
|
||
```java
|
||
public class FileSimpleCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, File> {
|
||
|
||
public static final FileSimpleCoder instance = new FileSimpleCoder();
|
||
|
||
@Override
|
||
public void convertTo(W out, File value) {
|
||
out.writeString(value == null ? null : value.getPath());
|
||
}
|
||
|
||
@Override
|
||
public File convertFrom(R in) {
|
||
String path = in.readString();
|
||
return path == null ? null : new File(path);
|
||
}
|
||
}
|
||
|
||
JsonFactory.root().register(File.class, FileSimpleCoder.instance);
|
||
|
||
ProtobufFactory.root().register(File.class, FileSimpleCoder.instance);
|
||
```
|
||
    2. 通过JavaBean类自定义静态方法自动加载:
|
||
```java
|
||
public class InnerCoderEntity {
|
||
|
||
private final String val;
|
||
|
||
private final int id;
|
||
|
||
private InnerCoderEntity(int id, String value) {
|
||
this.id = id;
|
||
this.val = value;
|
||
}
|
||
|
||
public static InnerCoderEntity create(int id, String value) {
|
||
return new InnerCoderEntity(id, value);
|
||
}
|
||
|
||
/**
|
||
* 该方法提供给Convert组件自动加载。
|
||
* 1) 方法名可以随意。
|
||
* 2) 方法必须是static
|
||
* 3)方法的参数有且只能有一个, 且必须是org.redkale.convert.ConvertFactory或子类。
|
||
* —3.1) 参数类型为org.redkale.convert.ConvertFactory 表示适合JSON,PROTOBUF。
|
||
* —3.2) 参数类型为org.redkale.convert.json.JsonFactory 表示仅适合JSON。
|
||
* —3.3) 参数类型为org.redkale.convert.pb.ProtobufFactory 表示仅适合PROTOBUF。
|
||
* 4)方法的返回类型必须是 Decodeable/Encodeable/SimpledCoder
|
||
* 若返回类型不是SimpledCoder, 就必须提供两个方法: 一个返回Decodeable 一个返回 Encodeable。
|
||
*
|
||
* @param factory
|
||
* @return
|
||
*/
|
||
private static SimpledCoder<Reader, Writer, InnerCoderEntity> createConvertCoder(final ConvertFactory factory) {
|
||
return new SimpledCoder<Reader, Writer, InnerCoderEntity>() {
|
||
|
||
//必须与EnMember[] 顺序一致
|
||
private final DeMember[] deMembers = new DeMember[]{
|
||
DeMember.create(factory, InnerCoderEntity.class, "id"),
|
||
DeMember.create(factory, InnerCoderEntity.class, "val")};
|
||
|
||
//必须与DeMember[] 顺序一致
|
||
private final EnMember[] enMembers = new EnMember[]{
|
||
EnMember.create(factory, InnerCoderEntity.class, "id"),
|
||
EnMember.create(factory, InnerCoderEntity.class, "val")};
|
||
|
||
@Override
|
||
public void convertTo(Writer out, InnerCoderEntity value) {
|
||
if (value == null) {
|
||
out.writeObjectNull(InnerCoderEntity.class);
|
||
return;
|
||
}
|
||
out.writeObjectB(value);
|
||
for (EnMember member : enMembers) {
|
||
out.writeObjectField(member, value);
|
||
}
|
||
out.writeObjectE(value);
|
||
}
|
||
|
||
@Override
|
||
public InnerCoderEntity convertFrom(Reader in) {
|
||
if (!in.readObjectB(this)) {
|
||
return null;
|
||
}
|
||
int index = 0;
|
||
final Object[] params = new Object[deMembers.length];
|
||
while (in.hasNext()) {
|
||
DeMember member = in.readFieldName(deMembers); //读取字段名
|
||
in.readBlank(); //读取字段名与字段值之间的间隔符,JSON则是跳过冒号:
|
||
if (member == null) {
|
||
in.skipValue(); //跳过不存在的字段的值, 一般不会发生
|
||
} else {
|
||
params[index++] = member.read(in);
|
||
}
|
||
}
|
||
in.readObjectE();
|
||
return InnerCoderEntity.create(params[0] == null ? 0 : (Integer) params[0], (String) params[1]);
|
||
}
|
||
};
|
||
}
|
||
|
||
public int getId() {
|
||
return id;
|
||
}
|
||
|
||
public String getVal() {
|
||
return val;
|
||
}
|
||
|
||
@Override
|
||
public String toString() {
|
||
return JsonConvert.root().convertTo(this);
|
||
}
|
||
}
|
||
```
|
||
|
||
## RestConvert
|
||
  ```@RestConvert```、```@RestConvertCoder```是针对```@RestService```接口进行自定义序列化注解
|
||
```java
|
||
@Data
|
||
public class RestConvertItem {
|
||
|
||
private long createTime;
|
||
|
||
@ConvertColumn(ignore = true)
|
||
private String aesKey;
|
||
}
|
||
|
||
@Data
|
||
public class RestConvertBean {
|
||
|
||
private int id;
|
||
|
||
private boolean enable;
|
||
|
||
private String name;
|
||
|
||
private RestConvertItem content;
|
||
}
|
||
|
||
//将boolean类型值转换成0/1的int值
|
||
public class RestConvertBoolCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Boolean> {
|
||
|
||
@Override
|
||
public void convertTo(W out, Boolean value) {
|
||
out.writeInt(value == null || !value ? 0 : 1);
|
||
}
|
||
|
||
@Override
|
||
public Boolean convertFrom(R in) {
|
||
return in.readInt() == 1;
|
||
}
|
||
}
|
||
|
||
@RestService(name = "test", autoMapping = true)
|
||
public class RestConvertService extends AbstractService {
|
||
|
||
//输出: {"content":{"createTime":100},"enable":true,"id":123,"name":"haha"}
|
||
public RestConvertBean load1() {
|
||
return createBean();
|
||
}
|
||
|
||
//输出: {"content":{"aesKey":"keykey","createTime":100},"enable":true,"id":123,"name":"haha"}
|
||
//aesKey字段也会被输出
|
||
@RestConvert(type = RestConvertItem.class, skipIgnore = true)
|
||
public RestConvertBean load2() {
|
||
return createBean();
|
||
}
|
||
|
||
//输出: {"id":123}
|
||
//只输出id字段
|
||
@RestConvert(type = RestConvertBean.class, onlyColumns = "id")
|
||
public RestConvertBean load3() {
|
||
return createBean();
|
||
}
|
||
|
||
//输出: {"content":{"createTime":100},"enable":1,"id":123,"name":"haha"}
|
||
//enable字段输出1,而不是true
|
||
@RestConvertCoder(type = RestConvertBean.class, field = "enable", coder = RestConvertBoolCoder.class)
|
||
public RestConvertBean load4() {
|
||
return createBean();
|
||
}
|
||
|
||
private RestConvertBean createBean() {
|
||
RestConvertBean bean = new RestConvertBean();
|
||
bean.setId(123);
|
||
bean.setName("haha");
|
||
bean.setEnable(true);
|
||
RestConvertItem item = new RestConvertItem();
|
||
item.setCreateTime(100);
|
||
item.setAesKey("keykey");
|
||
bean.setContent(item);
|
||
return bean;
|
||
}
|
||
}
|
||
```
|
||
|