0

I have a byte array:

[11, 10, 17, 05, 00, 0a, 01, 02]

and a class:

class abc{

    short var1;              // [11, 10]
    int var2;                // [17, 05, 00, 0a]
    byte var3;               // [01]
    byte var4;               // [02]

}

In python there is a lib ctypes where you can provide a byte array and it will encode it to a object of a class (definitely structure needs to be defined before hand).

I am looking something similar in java, basically what I am looking for a method which can encode and decode byte array.

[11, 10, 17, 05, 00, 0a, 01, 02] => (some method) => object abc: abc.getVar3() // value of var3 needs to be 1 as per above structure.

also object abc => (some method) => [11, 10, 17, 05, 00, 0a, 01, 02].

Only solution I can think of is lots of if else statement is there any better way to do it.

I have checked this answer it serialises entire class as byte object but I only want to encode data, basically inject data in object and also retrieve data from object in byte array.

Sri
  • 437
  • 1
  • 4
  • 13
ooo
  • 512
  • 1
  • 7
  • 27
  • I would recommend using Jackson and serialize it into JSON. If you want a custom encoding as you mentioned then you will most probably need to get your hands dirty. The encoding that you specified is not backward compatible and is bound to break when things change. – Aniket Sahrawat Feb 01 '21 at 09:43
  • ObjectInputStream, ObjectOutputStream? – Khanna111 Feb 01 '21 at 09:45

3 Answers3

1

DataInputStream allows you to read primitives from an input stream. Look out for following methods:

  • readShort()
  • readInt()
  • readByte()

Example code:

byte[] data = {0x11, 0x10, 0x17, 0x05, 0x00, 0x0a, 0x01, 0x02};

try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(data))) {
    abc value = new abc();
    value.var1 = in.readShort();
    value.var2 = in.readInt();
    value.var3 = in.readByte();
    value.var4 = in.readByte();
}
Peter Walser
  • 15,208
  • 4
  • 51
  • 78
  • This is similar solution what I am having right now I am slicing the array and creating desired data type and then inject that value in object, I have stored slice index and data type and to which variable to map it to in object in a map, but what I am looking for is that since I have already defined the class with its appropriate data types why can't I use that and inject values directly in that object similar to `memmove` method from python. – ooo Feb 01 '21 at 09:52
  • @ooo That's not possible because the memory layout of an object in Java is [left to the implementor's discretion](https://docs.oracle.com/javase/specs/jvms/se15/html/jvms-2.html#jvms-2.7). Therefore, you don't know whether var1 comes before or after var2; instead, you have to assign them individually. – k314159 Feb 01 '21 at 10:14
  • @k314159 is there any efficient way to do that, assuming you know before hand the class structure and when you get this byte array you can inject value in that. Also the ability to create the object and convert that to same byte array and send it. (Note that I am not talking to create byte array of entire class only of data in object) – ooo Feb 01 '21 at 10:23
0

There's several ways to do that. Here's a simple one:

byte[] values = [11, 10, 17, 05, 00, 0a, 01, 02]; //WARNING: update 0a to the right value in java

The class definition (class name start with uppercase in java):

class Abc{

    short[] var1;              // [11, 10]
    int[] var2;                // [17, 05, 00, 0a]
    byte var3;               // [01]
    byte var4;               // [02]
    
    public Abc(byte[] values){
      var1 = new short[]{(short)values[0],(short)values[1]};
      var2 = new int[]{(int)values[2],(int)values[3],(int)values[4],(int)values[5]};
      var3 = values[6];
      var4 = values[7];

    }
    
    public byte[] getBytes(){
      return new byte[]{ (byte)var1[0], (byte)var1[1], (byte)var2[0],(byte)var2[1], (byte)var2[2], (byte)var2[3], var3, var4};
    }

}

So to create a new object (objects are lowercase):

Abc abc = new Abc(values);
byte[] result = abc.getBytes();
0

Assuming you have looked at Serialization Utils from Apache Commons, you could understand that there should be some sort of class based object to be an input for any serialization algorithm.

However you want to avoid having a separate dedicated class to store and retrieve data (if I'm wrong clarify me). One possible workaround is to use a built-in class in java to encapsulate the data for you.

For example you could put your data fields in a List (ArrayList) or even better a Map (HashMap). Then you could serialize the map object so that it can be passed around. The good thing is these classes are already built into java so you don't need to create a separate class just for the sake of serialization.

Here is a sample example code:

import org.apache.commons.lang3.SerializationUtils;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

class Abc {

    short var1;              // [11, 10]
    int var2;                // [17, 05, 00, 0a]
    byte var3;               // [01]
    byte var4;               // [02]

    public Map<String, Object> toMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("var1", var1);
        map.put("var2", var2);
        map.put("var3", var3);
        map.put("var4", var4);

        return map;
    }

    public byte[] serializeMap() {
        return SerializationUtils.serialize((Serializable) toMap());
    }

    public Map<String, Object> deSerializeMap(byte[] serializedObject) {
        return SerializationUtils.deserialize(serializedObject);
    }

    public void setParams(Map<String, Object> deSerializedObject) {
        this.var1 = (short) deSerializedObject.get("var1");
        this.var2 = (int) deSerializedObject.get("var2");
        this.var3 = (byte) deSerializedObject.get("var3");
        this.var4 = (byte) deSerializedObject.get("var4");
    }
}

public class Test2 {
    public static void main(String[] args) {

        Abc obj = new Abc();
        obj.var1 = 10000;
        obj.var2 = 500000;
        obj.var3 = 2;
        obj.var4 = 30;

        Abc newObj = new Abc();
        newObj.setParams(newObj.deSerializeMap(obj.serializeMap()));

        System.out.printf("var1: %s\nvar2: %s\nvar3: %s\nvar4: %s\n", newObj.var1, newObj.var2, newObj.var3, newObj.var4);
    }
}

As you can see here, I use obj to create the values, then serialize it and newObj to deserialize and assign back to the variable.

obj could be belong to one class and you could serialize and send the values through network and can deserialize them on another object. This is possible because the HashMap is already built into Java.

Good thing here is that you don't need to have same class in both places. As long as both classes have above variables (variables don't even have to have a same name either, as long as type matches, this will work), you could implement this solution.

ahrooran
  • 931
  • 1
  • 10
  • 25
  • Is the same thing possible only by using java core lib? – ooo Feb 01 '21 at 10:09
  • @ooo it is perfectly possible. You already looked at those answers you mentioned. There are already many answers which only uses built in core modules. Just create another method which utilizes one of those answers and you can come up with your own way of serialization and deserialization without any third party modules – ahrooran Feb 01 '21 at 18:20