11

I would like to develop restful service and it will return JSON String to client. Now there is byte[] attribute in my object.

I use ObjectMapper to translate this object to json and response to client. But I find the byte[] is wrong if I use String.getBytes() to translate the received string. Following is example.

Pojo class

public class Pojo {
    private byte[] pic;
    private String id;
    //getter, setter,...etc
}

Prepare data: use image to get byte array

InputStream inputStream = FileUtils.openInputStream(new File("config/images.jpg"));
byte[] pic = IOUtils.toByteArray(inputStream);
Pojo pojo = new Pojo();
pojo.setId("1");
pojo.setPic(pic);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(pojo);

--Situation 1: use readvalue to object => the image2.jpg is correct

Pojo tranPojo = mapper.readValue(json, Pojo.class);

byte[] tranPicPojo = tranPojo.getPic();
InputStream isPojo = new ByteArrayInputStream(tranPicPojo);
File tranFilePojo = new File("config/images2.png");
FileUtils.copyInputStreamToFile(isPojo, tranFilePojo);

--Situation 2: use readvalue to Map and get String => the image3.jpg is broken

Map<String, String> map = mapper.readValue(json, Map.class);

byte[] tranString = map.get("pic").getBytes();
InputStream isString = new ByteArrayInputStream(tranString);
File tranFileString = new File("config/images3.png");
FileUtils.copyInputStreamToFile(isString, tranFileString);

If I have to use situation 2 to translate the JSON String, how can I do it? Because clients can not get the Pojo.class, so clients only can translate the JSON string themselves.

Thanks a lot!

Ringing.Wang
  • 123
  • 1
  • 1
  • 5

2 Answers2

7

Jackson is serializing byte[] as a Base64 string as describe in the documentation of the serializer.

The default base64 variant is the MIME without line feed (everything in one line).

You can change the variant by using the setBase64Variant on the ObjectMapper.

blacktide
  • 10,654
  • 8
  • 33
  • 53
Kazaag
  • 2,115
  • 14
  • 14
  • Thanks a lot! I try to add following statements and the saved image is correct! Base64 base64 = new Base64(); byte[] tranString = base64.decode(map.get("pic").getBytes()); – Ringing.Wang Oct 15 '16 at 02:15
4

ObjectMapper handles this, expressed through a unit-test:

@Test
public void byteArrayToAndFromJson() throws IOException {
    final byte[] bytes = { 1, 2, 3, 4, 5 };

    final ObjectMapper objectMapper = new ObjectMapper();

    final byte[] jsoned = objectMapper.readValue(objectMapper.writeValueAsString(bytes), byte[].class);

    assertArrayEquals(bytes, jsoned);
}

This is jackson 2.x btw..

Here is how to send a file to the browser using Jersey:

@GET
@Path("/documentFile")
@Produces("image/jpg")
public Response getFile() {
    final byte[] yourByteArray = ...;
    final String yourFileName = ...;
    return Response.ok(yourByteArray)
                   .header("Content-Disposition", "inline; filename=\"" + yourFilename + "\";")
                   .build();

Another example, using Spring MVC:

@RequestMapping(method = RequestMethod.GET)
public void getFile(final HttpServletResponse response) {
    final InputStream yourByteArrayAsStream = ...;
    final String yourFileName = ...;

    response.setContentType("image/jpg");
    try {
        output = response.getOutputStream();
        IOUtils.copy(yourByteArrayAsStream, output);
    } finally {
        IOUtils.closeQuietly(yourByteArrayAsStream);
        IOUtils.closeQuietly(output);
    }
}
Tobb
  • 11,850
  • 6
  • 52
  • 77
  • Thanks for your response. But if frond-end javascript clients call this API to receive the JSON, does they can translate the String to byte[] correctly without ObjectMapper? Beside, how does Jackson ObjectMapper translate the byte[] to text string? Thanks! – Ringing.Wang Oct 13 '16 at 15:09
  • 1
    I don't see your question having anything to do with javascript? When sending a file to a browser, one does not convert it to a string (including json). You should have a separate url for getting the file, and copy it (or a stream of it) to the response, with the appropriate headers (mime type and so on). – Tobb Oct 13 '16 at 16:14
  • Because we store the file as byte[] in DB, we have to select it and response to frond-end to display with extra information(ex. title, description...etc). So we try to use JSON to response all information in the beginning. Thanks for your suggestion! We will discuss this solution. – Ringing.Wang Oct 15 '16 at 02:11
  • You would then usually have 2 methods, one for getting the metadata for the file (title, description, maybe url to the actual file), but not the file itself. The second method will be one for fetching the actual file. – Tobb Oct 15 '16 at 09:47