java对象转成byte数组的三种方法

java对象转成byte数组,在使用netty进行通信协议传输的场景中是非常常见的。比如,协议有一些定好的协议头、classid,messageid等等信息,还有一个关键的内容是payload。不同的协议内容都会放到payload中,而这个payload往往就是一个byte数组。

那么,如何方便的将一个java对象构造成一个byte数组呢?

1 bytebuf填充

我们以下面这个对象举例:

public class UgvData implements Serializible{
    private static final long serialVersionUID = -219988432063763456L;

    //状态码
    byte status;
    //当前GPS经度
    float longitude;
    //当前GPS纬度
    float latitude;
    //行驶速度 单位是 m/s,带一个小数点
    float speed;
    //当前电量百分比
    short batteryPercentage;
    //任务编号
    long quest;

    public byte[] toByteArray() {
        ByteBuf buf = Unpooled.buffer(32);
        buf.writeByte(this.getStatus());
        buf.writeFloat(getLongitude());
        buf.writeFloat(getLatitude());
        buf.writeFloat(getSpeed());
        buf.writeShort(getBatteryPercentage());
        buf.writeLong(getQuest());
        return buf.array();
    }

    //省略get set
}

那么只需要new出一个上面的对象,调用其toByteArray方法,即可将这个对象转成byte数组。

2 巧用json

我们都知道,字符串是可以转成byte数组的。将一个对象转成json字符串也很容易,直接使用fastjson就可以了。如果对fastjson使用有问题的,可以看我的另一篇博客JSON.parseObject 和 JSON.toJSONString 实例

JSON.toJsonString(ugvData).getBytes()

3 反射的方式

第一种方法的缺点在于,每一个类都要这么写一个toByteArray方法。如果类多了是非常麻烦的。有什么方便的方法吗?当然是有的,利用反射的方式(只会在第一次反射,后面会做本地缓存,所以性能开销不大)。需要在一个文件夹下添加下面五个类

1.Codecable

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Lists;
import lombok.Data;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@Data
public abstract class Codecable {

    public static List resolveFileldWrapperList(Class clazz){
        Field[] fields = clazz.getDeclaredFields();
        List fieldWrapperList = Lists.newArrayList();
        for (Field field : fields) {
            CodecProprety codecProprety = field.getAnnotation(CodecProprety.class);
            if (codecProprety == null) {
                continue;
            }
            FieldWrapper fw = new FieldWrapper(field, codecProprety);
            fieldWrapperList.add(fw);
        }

        Collections.sort(fieldWrapperList, new Comparator() {
            @Override
            public int compare(FieldWrapper o1, FieldWrapper o2) {
                return o1.getCodecProprety().order() - o2.getCodecProprety().order();
            }
        });

        return fieldWrapperList;
    }

    @JsonIgnore
    public abstract List getFieldWrapperList();
}

2.CodecProprety

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface CodecProprety {
    /**
     * 属性顺序
     * @return
     */
    int order();

    /**
     * 数据长度。解码时用,除了简单数据类型之外才起作用(如:String)。
     * @return
     */
    int length() default 0;
}

3.FieldWrapper

import lombok.AllArgsConstructor;
import lombok.Data;

import java.lang.reflect.Field;
@Data
@AllArgsConstructor
public class FieldWrapper {
    /**
     * 上下行数据属性
     */
    private Field field;
    /**
     * 上下行数据属性上的注解
     */
    private CodecProprety codecProprety;
}

4.PayloadDecoder

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.List;

public class PayloadDecoder {

    public static  T resolve(byte[] src, Class clazz) {
        T instance = null;
        try {
            instance = clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("实例化类失败", e);
        }

        List fieldWrapperList = instance.getFieldWrapperList();
        ByteBuf buffer = Unpooled.buffer().writeBytes(src);
        for (FieldWrapper fieldWrapper : fieldWrapperList) {
            fillData(fieldWrapper, instance, buffer);
        }

        return instance;
    }

    private static void fillData(FieldWrapper fieldWrapper, Object instance, ByteBuf buffer) {
        Field field = fieldWrapper.getField();
        field.setAccessible(true);
        String typeName = field.getType().getName();
        try {
            switch (typeName) {
                case "java.lang.Boolean":
                case "boolean":
                    boolean b = buffer.readBoolean();
                    field.set(instance, b);
                    break;

                case "java.lang.Character":
                case "char":
                    CharSequence charSequence = buffer.readCharSequence(fieldWrapper.getCodecProprety().length(), Charset.forName("UTF-8"));
                    field.set(instance, charSequence);
                    break;
                case "java.lang.Byte":
                case "byte":
                    byte b1 = buffer.readByte();
                    field.set(instance, b1);
                    break;
                case "java.lang.Short":
                case "short":
                    short readShort = buffer.readShort();
                    field.set(instance, readShort);
                    break;
                case "java.lang.Integer":
                case "int":
                    int readInt = buffer.readInt();
                    field.set(instance, readInt);
                    break;
                case "java.lang.Long":
                case "long":
                    long l = buffer.readLong();
                    field.set(instance, l);
                    break;
                case "java.lang.Float":
                case "float":
                    float readFloat = buffer.readFloat();
                    field.set(instance, readFloat);
                    break;
                case "java.lang.Double":
                case "double":
                    double readDouble = buffer.readDouble();
                    field.set(instance, readDouble);
                    break;
                case "java.lang.String":
                    String readString = buffer.readCharSequence(fieldWrapper.getCodecProprety().length(), Charset.forName("UTF-8")).toString();
                    field.set(instance, readString);
                    break;
                default:
                    throw new RuntimeException(typeName + "不支持,bug");
            }
        } catch (Exception e) {
            throw new RuntimeException(typeName + "读取失败,field:" + field.getName(), e);
        }
    }


}

5.PayloadEncoder

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.List;

public class PayloadEncoder {

    public static  byte[] getPayload(T command) {
        List fieldWrapperList = command.getFieldWrapperList();
        ByteBuf buffer = Unpooled.buffer();
        fieldWrapperList.forEach(fieldWrapper -> write2ByteBuf(fieldWrapper, command, buffer));
        return buffer.array();
    }

    /**
     * 数据写入到ByteBuf
     *
     * @param fieldWrapper
     * @param instance
     * @param buffer
     */
    private static void write2ByteBuf(FieldWrapper fieldWrapper, Object instance, ByteBuf buffer) {
        Field field = fieldWrapper.getField();
        String typeName = field.getType().getName();
        field.setAccessible(true);
        Object value = null;
        try {
            value = field.get(instance);
        } catch (IllegalAccessException e) {
            new RuntimeException("反射获取值失败,filed:" + field.getName(), e);
        }
        switch (typeName) {
            case "java.lang.Boolean":
            case "boolean":
                buffer.writeBoolean((Boolean) value);
                break;
            case "java.lang.Character":
            case "char":
                buffer.writeCharSequence((CharSequence) value, Charset.forName("UTF-8"));
                break;
            case "java.lang.Byte":
            case "byte":
                buffer.writeByte((byte) value);
                break;
            case "java.lang.Short":
            case "short":
                buffer.writeShort((short) value);
                break;
            case "java.lang.Integer":
            case "int":
                buffer.writeInt((int) value);
                break;
            case "java.lang.Long":
            case "long":
                buffer.writeLong((long) value);
                break;
            case "java.lang.Float":
            case "float":
                buffer.writeFloat((float) value);
                break;
            case "java.lang.Double":
            case "double":
                buffer.writeDouble((double) value);
                break;
            case "java.lang.String":
                buffer.writeCharSequence((CharSequence) value, Charset.forName("UTF-8"));
                break;
            default:
                throw new RuntimeException(typeName + "不支持,bug");
        }
    }
}

添加完上面五个类之后,使用也很简单,只需要如下所示,就可以把driveStartData转成byte数组。

PayloadEncoder.getPayload(driveStartData)

4 总结

可能会有人问了,上面三种,明显第二种转json最简单,为什么还要用另外两种呢?

其实,第一种和第三种可以归为一类,都是把对象直接转成byte数组,下一层做解析的话,可以一个一个元素取;
第二种情况是把对象的json字符串转成byte数组,问题就在于,json字符串最开头是”{“,也就是转成的byte数组的第一位是”{“对应的数值

在使用中应该根据情况来,如果下一层做解析是直接取元素,对象少的话用第一种;对象多的话用第三种;
如果下一层做了排除掉json的一些格式的解析,就用第二种

说明
如有转载,请注明出处
https://blog.csdn.net/antony9118/article/details/80713348

你可能感兴趣的:(网络)