views:

21353

answers:

4

I have binary data in a file that I can read into a byte array and process with no problem. Now I need to send parts of the data over a network connection as elements in an XML document. My problem is that when I convert the data from an array of bytes to a String and back to an array of bytes, the data is getting corrupted. I've tested this on one machine to isolate the problem to the String conversion, so I now know that it isn't getting corrupted by the XML parser or the network transport.

What I've got right now is

byte[] buffer = ...; // read from file
// a few lines that prove I can process the data successfully
String element = new String(buffer);
byte[] newBuffer = element.getBytes();
// a few lines that try to process newBuffer and fail because it is not the same data anymore

Does anyone know how to convert binary to String and back without data loss?

Answered: Thanks Sam. I feel like an idiot. I had this answered yesterday because my SAX parser was complaining. For some reason when I ran into this seemingly separate issue, it didn't occur to me that it was a new symptom of the same problem.

EDIT: Just for the sake of completeness, I used the Base64 class from the Apache Commons Codec package to solve this problem.

+7  A: 

If you encode it in base64, this will turn any data into ascii safe text, but base64 encoded data is larger than the orignal data

Sam
A: 

How are you building your XML document? If you use java's built in XML classes then the string encoding should be handled for you.

Take a look at the javax.xml and org.xml packages. That's what we use for generating XML docs, and it handles all the string encoding and decoding quite nicely.

---EDIT:

Hmm, I think I misunderstood the problem. You're not trying to encode a regular string, but some set of arbitrary binary data? In that case the Base64 encoding suggested in an earlier comment is probably the way to go. I believe that's a fairly standard way of encoding binary data in XML.

Herms
+1  A: 

See this question, http://stackoverflow.com/questions/19893 Instead of converting the byte[] into String then pushing into XML somewhere, convert the byte[] to a String via BASE64 encoding (some XML libraries have a type to do this for you). The BASE64 decode once you get the String back from XML.

Use http://commons.apache.org/codec/

You data may be getting messed up due to all sorts of weird character set restrictions and the presence of non-priting characters. Stick w/ BASE64.

basszero
+10  A: 

String(byte[]) treats the data as the default character encoding. So, how bytes get converted from 8-bit values to 16-bit Java Unicode chars will vary not only between operating systems, but can even vary between different users using different codepages on the same machine! This constructor is only good for decoding one of your own text files. Do not try to convert arbitrary bytes to chars in Java!

Encoding as base64 is a good solution. This is how files are sent over SMTP (e-mail). The (free) Apache Commons Codec project will do the job.

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.codec.binary.Base64;

public class EncodeDecode {

    private static void copy(InputStream in, OutputStream out) throws IOException {
     byte[] barr = new byte[1024];
     while(true) {
      int r = in.read(barr);
      if(r <= 0) {
       break;
      }
      out.write(barr, 0, r);
     }
    }

    private static byte[] loadFile(File file) throws IOException {
     InputStream in = new FileInputStream(file);
     try {
      ByteArrayOutputStream buffer = new ByteArrayOutputStream();
      copy(in, buffer);
      return buffer.toByteArray();
     } finally {
      in.close();
     }
    }

    public static void main(String[] args) throws Exception {
     File file = new File("/bin/ls");
     byte[] bytes = loadFile(file);

     //all chars in encoded are guaranteed to be 7-bit ASCII
     byte[] encoded = Base64.encodeBase64(bytes);

     String printMe = new String(encoded, "ASCII");
     System.out.println(printMe);

     byte[] decoded = Base64.decodeBase64(encoded);

     // check
     for (int i = 0; i < bytes.length; i++) {
      assert bytes[i] == decoded[i];
     }
    }

}
McDowell