bytearray - How do I convert mixed Java data types into a Java byte array?

I need to construct a Java byte array out of mixed data types, but I don't know how to do this. These are my types:

byte version = 1; // at offset 0
short message_length = // the size of the byte[] message I am constructing here, at offset 1
short sub_version = 15346; // at offset 3
byte message_id = 2; // at offset 5
int flag1 = 10; // at offset 6
int flag2 = 0; // at offset 10
int flag3 = 0; // at offset 14
int flag4 = 0; // at offset 18
String message = "the quick brown fox jumps over the lazy dog"; // at offset 22

I know for the String, I can use

message.getBytes("US_ASCII");

I know for the int values, I can use

Integer.byteValue();

I know for the short values, I can use

Short.byteValue();

And the byte values are already bytes, I am just not sure of how to combine all of these into a single byte array. I have read about

System.arraycopy();

Is this the correct process, I just convert all the data to bytes, and start "concatenating" the byte array with arraycopy?

I am communicating with some distant server I have no control over, and this is the message process they require.

4 Answers

  1. Benson- Reply

    2019-11-16

    Wrap a DataOutputStream around a ByteArrayOutputStream. This way you can write all the primitive types like int and short directly to the DataOutputStream, which converts them to bytes and forwards them to the ByteArrayOutputStream, from which you can then retrieve the whole thing as one byte array:

    ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    DataOutputStream dOut = new DataOutputStream(bOut);
    
    dOut.writeByte(version);
    dOut.writeShort(message_length);
    dOut.writeShort(sub_version);
    dOut.writeByte(message_id);
    dOut.writeInt(flag1);
    dOut.writeInt(flag2);
    dOut.writeInt(flag3);
    dOut.writeInt(flag4);
    dOut.write(message.getBytes(), 0, message.length());
    
    dOut.flush();
    byte[] result = bOut.toByteArray();
    

    The best thing about this is that you can do the exact opposite (extracting values from a byte array) with DataInputStream and ByteArrayInputStream completely analoguously to the above code.

  2. Bill- Reply

    2019-11-16

    If by a 'mixed type' you mean a class with different member field types, then one approach is to make your class serializable, and use ApacheUtils

    byte[] data = SerializationUtils.serialize(yourObject);
    
  3. Bob- Reply

    2019-11-16

    Certainly you can concatenate these values with arrayCopy, as you've suggested.

    You can also append your bytes onto a ByteArrayOutputStream.

    The key is to understand exactly what the receiving system is expecting. How does it know where one field ends and the next begins? How does it know what type it's reading at a given position in the stream? There are lots of ways they could have chosen to do that - with length headers in the protocol; with type headers; with null-termination of strings; with a set order of fields and their lengths; and so on.

    Whatever method you choose, write unit tests that check for edge cases like negative numbers, very large numbers, non-ASCII text and so on. It's easy to get stung when everything has been working fine, then suddenly the server chokes on a Unicode character or a negative number that it interprets as a very large number.

    One other option -- perhaps slight overkill for your needs, but flexible and with high performance -- is Google's protocol buffers library.

  4. Brandon- Reply

    2019-11-16

    All, I wanted to post my own solution to my problem here. I did a quick Google search on how to insert a short into java byte array. One of the results talked about a Java ByteBuffer. After some reading, I determined this was the best and quickest way for me to get the results I needed. One section in the Java API that really made me interested in the ByteBuffer was this:

    Methods in this class that do not otherwise have a value to return are specified to return the buffer upon which they are invoked. This allows method invocations to be chained. The sequence of statements

    bb.putInt(0xCAFEBABE);
    bb.putShort(3);
    bb.putShort(45);
    

    can, for example, be replaced by the single statement

    bb.putInt(0xCAFEBABE).putShort(3).putShort(45);
    

    So, that is what I did:

    byte version = 1;
    short message_length = 72;
    short sub_version = 15346;
    byte message_id = 2;
    int flag1 = 10;
    int flag2 = 0;
    int flag3 = 0;
    int flag4 = 0;
    String message = "the quick brown fox jumps over the lazy dog";
    ByteBuffer messageBuffer = ByteBuffer.allocate(message_length);
    
    messageBuffer.put(version).putShort(message_length).putShort(sub_version).put(message_id).putInt(flag1).putInt(flag2).putInt(flag3).putInt(flag4).put(message.getBytes());
    
    byte[] myArray = messageBuffer.array();
    

    That was fast and easy, and just what I needed. Thank you all who took the time to read and reply.

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>