Java tutorial
// Copyright 2010-2011 Michel Kraemer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.undercouch.bson4jackson; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; import java.util.regex.Pattern; import org.bson.BSONDecoder; import org.bson.BSONObject; import org.bson.BasicBSONDecoder; import org.bson.types.BSONTimestamp; import org.bson.types.Code; import org.bson.types.CodeWScope; import org.junit.Test; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import de.undercouch.bson4jackson.io.DynamicOutputBuffer; import de.undercouch.bson4jackson.types.JavaScript; import de.undercouch.bson4jackson.types.ObjectId; import de.undercouch.bson4jackson.types.Timestamp; /** * Tests {@link BsonGenerator} * @author Michel Kraemer */ public class BsonGeneratorTest { @Test public void generatePrimitives() throws Exception { Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("Int32", 5); data.put("Boolean1", true); data.put("Boolean2", false); data.put("String", "Hello"); data.put("Long", 1234L); data.put("Null", null); data.put("Float", 1234.1234f); data.put("Double", 5678.5678); //BigInteger that can be serialized as an Integer data.put("BigInt1", BigInteger.valueOf(Integer.MAX_VALUE)); //BigInteger that can be serialized as a Long BigInteger bi2 = BigInteger.valueOf(Integer.MAX_VALUE).multiply(BigInteger.valueOf(2)); data.put("BigInt2", bi2); //BigInteger that will be serialized as a String BigInteger bi3 = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.valueOf(Long.MAX_VALUE)); data.put("BigInt3", bi3); BSONObject obj = generateAndParse(data); assertEquals(5, obj.get("Int32")); assertEquals(true, obj.get("Boolean1")); assertEquals(false, obj.get("Boolean2")); assertEquals("Hello", obj.get("String")); assertEquals(1234L, obj.get("Long")); assertEquals(null, obj.get("Null")); assertEquals(1234.1234f, (Double) obj.get("Float"), 0.00001); assertEquals(5678.5678, (Double) obj.get("Double"), 0.00001); assertEquals(Integer.MAX_VALUE, obj.get("BigInt1")); assertEquals(Long.valueOf(Integer.MAX_VALUE) * 2L, obj.get("BigInt2")); assertEquals(bi3.toString(), obj.get("BigInt3")); } @Test public void stream() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BsonFactory fac = new BsonFactory(); fac.enable(BsonGenerator.Feature.ENABLE_STREAMING); BsonGenerator gen = fac.createGenerator(baos); byte[] dummy = new byte[DynamicOutputBuffer.DEFAULT_BUFFER_SIZE * 3 / 2]; gen.writeStartObject(); gen.writeFieldName("Test"); gen.writeBinary(dummy); assertEquals(DynamicOutputBuffer.DEFAULT_BUFFER_SIZE, baos.size()); gen.writeEndObject(); gen.writeStartObject(); gen.writeFieldName("Test"); gen.writeBinary(dummy); assertEquals(DynamicOutputBuffer.DEFAULT_BUFFER_SIZE * 3, baos.size()); gen.writeEndObject(); gen.close(); assertTrue(baos.size() > DynamicOutputBuffer.DEFAULT_BUFFER_SIZE * 3); } private void assertRaw(byte[] r) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(r); BSONDecoder decoder = new BasicBSONDecoder(); BSONObject obj = decoder.readObject(bais); byte[] o = (byte[]) obj.get("Test"); CharBuffer buf = ByteBuffer.wrap(o).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(); assertEquals(2, buf.remaining()); char a = buf.get(); char b = buf.get(); assertEquals('a', a); assertEquals('b', b); } @Test public void rawChar() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BsonGenerator gen = new BsonGenerator(JsonGenerator.Feature.collectDefaults(), 0, baos); gen.writeStartObject(); gen.writeFieldName("Test"); gen.writeRaw(new char[] { 'a', 'b' }, 0, 2); gen.writeEndObject(); gen.close(); assertRaw(baos.toByteArray()); } @Test public void rawString() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BsonGenerator gen = new BsonGenerator(JsonGenerator.Feature.collectDefaults(), 0, baos); gen.writeStartObject(); gen.writeFieldName("Test"); gen.writeRaw("ab"); gen.writeEndObject(); gen.close(); assertRaw(baos.toByteArray()); } @Test public void rawBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BsonGenerator gen = new BsonGenerator(JsonGenerator.Feature.collectDefaults(), 0, baos); gen.writeStartObject(); gen.writeFieldName("Test"); gen.writeBinary(new byte[] { (byte) 1, (byte) 2 }); gen.writeEndObject(); gen.close(); byte[] r = baos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(r); BSONDecoder decoder = new BasicBSONDecoder(); BSONObject obj = decoder.readObject(bais); byte[] o = (byte[]) obj.get("Test"); assertEquals(2, o.length); assertEquals((byte) 1, o[0]); assertEquals((byte) 2, o[1]); } @Test public void stackedObjects() throws Exception { Map<String, Object> data1 = new LinkedHashMap<String, Object>(); data1.put("Int32", 5); Map<String, Object> data3 = new LinkedHashMap<String, Object>(); data3.put("String", "Hello"); Map<String, Object> data2 = new LinkedHashMap<String, Object>(); data2.put("Int64", 10L); data2.put("data1", data1); data2.put("data3", data3); BSONObject obj2 = generateAndParse(data2); assertEquals(10L, obj2.get("Int64")); BSONObject obj1 = (BSONObject) obj2.get("data1"); assertEquals(5, obj1.get("Int32")); BSONObject obj3 = (BSONObject) obj2.get("data3"); assertEquals("Hello", obj3.get("String")); } @Test @SuppressWarnings("unchecked") public void arrays() throws Exception { Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("Int32", 5); data.put("Arr", Arrays.asList("a", "b", "c")); data.put("Int64", 10L); List<String> a3 = Arrays.asList("d", "e", "f"); List<String> a4 = Arrays.asList("g", "h", "j"); List<List<String>> a5 = new ArrayList<List<String>>(); a5.add(a3); a5.add(a4); data.put("Arr2", a5); Map<String, Object> data2 = new LinkedHashMap<String, Object>(); data2.put("Str", "Hello"); List<Map<String, Object>> a6 = new ArrayList<Map<String, Object>>(); a6.add(data2); data.put("Arr3", a6); BSONObject obj = generateAndParse(data); assertEquals(5, obj.get("Int32")); List<String> o = (List<String>) obj.get("Arr"); assertEquals(3, o.size()); assertEquals("a", o.get(0)); assertEquals("b", o.get(1)); assertEquals("c", o.get(2)); assertEquals(10L, obj.get("Int64")); List<List<String>> o5 = (List<List<String>>) obj.get("Arr2"); assertEquals(a5, o5); List<BSONObject> o6 = (List<BSONObject>) obj.get("Arr3"); assertEquals(1, o6.size()); BSONObject b6 = o6.get(0); assertEquals("Hello", b6.get("Str")); } @Test public void utf8Strings() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BsonGenerator gen = new BsonGenerator(JsonGenerator.Feature.collectDefaults(), 0, baos); gen.writeStartObject(); gen.writeFieldName("a\u20AC\u00A2\u00A2bb"); gen.writeString("a\u20AC\u00A2\u00A2bb"); gen.writeEndObject(); gen.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); BSONDecoder decoder = new BasicBSONDecoder(); BSONObject obj = decoder.readObject(bais); String s = (String) obj.get("a\u20AC\u00A2\u00A2bb"); assertEquals("a\u20AC\u00A2\u00A2bb", s); } @Test public void uuids() throws Exception { Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("Int32", 5); data.put("Arr", Arrays.asList("a", "b", "c")); UUID uuid = UUID.randomUUID(); data.put("Uuid", uuid); BSONObject obj = generateAndParse(data); assertEquals(5, obj.get("Int32")); assertNotNull(obj.get("Uuid")); assertEquals(UUID.class, obj.get("Uuid").getClass()); assertEquals(uuid, obj.get("Uuid")); } @Test public void dates() throws Exception { Map<String, Object> data = new LinkedHashMap<String, Object>(); Date date = new Date(); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); data.put("date", date); data.put("calendar", calendar); BSONObject obj = generateAndParse(data); assertEquals(date, obj.get("date")); assertEquals(date, obj.get("calendar")); } @Test public void objectIds() throws Exception { Map<String, Object> data = new LinkedHashMap<String, Object>(); ObjectId objectId = new ObjectId((int) (System.currentTimeMillis() / 1000), new Random().nextInt(), 100); data.put("_id", objectId); BSONObject obj = generateAndParse(data); org.bson.types.ObjectId result = (org.bson.types.ObjectId) obj.get("_id"); assertNotNull(result); assertEquals(objectId.getTime(), result.getTimeSecond()); assertEquals(objectId.getMachine(), result.getMachine()); assertEquals(objectId.getInc(), result.getInc()); } @Test public void patterns() throws Exception { Pattern pattern = Pattern.compile("a.*a", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("pattern", pattern); BSONObject obj = generateAndParse(data); Pattern result = (Pattern) obj.get("pattern"); assertNotNull(result); assertEquals(pattern.pattern(), result.pattern()); assertEquals(pattern.flags(), result.flags()); } @Test public void timestamps() throws Exception { Timestamp timestamp = new Timestamp(100, 200); Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("timestamp", timestamp); BSONObject obj = generateAndParse(data); BSONTimestamp result = (BSONTimestamp) obj.get("timestamp"); assertNotNull(result); assertEquals(timestamp.getInc(), result.getInc()); assertEquals(timestamp.getTime(), result.getTime()); } @Test public void javascript() throws Exception { JavaScript javaScript = new JavaScript("a < 100"); Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("javaScript", javaScript); BSONObject obj = generateAndParse(data); Code result = (Code) obj.get("javaScript"); assertNotNull(result); assertEquals(javaScript.getCode(), result.getCode()); } @Test public void javascriptWithScope() throws Exception { Map<String, Object> scope = new LinkedHashMap<String, Object>(); scope.put("a", 99); scope.put("b", 80); JavaScript javaScript = new JavaScript("a < 100", scope); Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("javaScript", javaScript); BSONObject obj = generateAndParse(data); CodeWScope result = (CodeWScope) obj.get("javaScript"); assertNotNull(result); assertEquals(javaScript.getCode(), result.getCode()); Map<?, ?> returnedScope = result.getScope().toMap(); assertEquals(returnedScope, scope); } private BSONObject generateAndParse(Map<String, Object> data, BsonGenerator.Feature... featuresToEnable) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BsonFactory bsonFactory = new BsonFactory(); if (featuresToEnable != null) { for (BsonGenerator.Feature fe : featuresToEnable) { bsonFactory.enable(fe); } } ObjectMapper om = new ObjectMapper(bsonFactory); om.registerModule(new BsonModule()); om.writeValue(baos, data); byte[] r = baos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(r); BSONDecoder decoder = new BasicBSONDecoder(); return decoder.readObject(bais); } @Test public void writeBigDecimalsAsStrings() throws Exception { Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("big", new BigDecimal("0.3")); BSONObject obj = generateAndParse(data); Double result = (Double) obj.get("big"); //BigDecimal("0.3") does not equal 0.3! assertEquals(Double.valueOf(0.3), result, 0.000001); assertFalse(Double.valueOf(0.3).equals(result)); data = new LinkedHashMap<String, Object>(); data.put("big", new BigDecimal("0.3")); obj = generateAndParse(data, BsonGenerator.Feature.WRITE_BIGDECIMALS_AS_STRINGS); String strResult = (String) obj.get("big"); assertEquals("0.3", strResult); } @Test public void writeBinaryData() throws Exception { byte[] binary = new byte[] { (byte) 0x05, (byte) 0xff, (byte) 0xaf, (byte) 0x30, 'A', 'B', 'C', (byte) 0x13, (byte) 0x80, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; Map<String, Object> data = new LinkedHashMap<String, Object>(); data.put("binary", binary); //binary data has to be converted to base64 with normal JSON ObjectMapper mapper = new ObjectMapper(); String jsonString = mapper.writeValueAsString(data); assertEquals("{\"binary\":\"Bf+vMEFCQxOA/////w==\"}", jsonString); //with BSON we don't have to convert to base64 ByteArrayOutputStream baos = new ByteArrayOutputStream(); BsonFactory bsonFactory = new BsonFactory(); ObjectMapper om = new ObjectMapper(bsonFactory); om.writeValue(baos, data); //document header (4 bytes) + type (1 byte) + field_name ("binary", 6 bytes) + //end_of_string (1 byte) + binary_size (4 bytes) + subtype (1 byte) + //binary_data (13 bytes) + end_of_document (1 byte) int expectedLen = 4 + 1 + 6 + 1 + 4 + 1 + 13 + 1; assertEquals(expectedLen, baos.size()); //BSON is smaller than JSON (at least in this case) assertTrue(baos.size() < jsonString.length()); //test if binary data can be parsed BSONObject obj = generateAndParse(data); byte[] objbin = (byte[]) obj.get("binary"); assertArrayEquals(binary, objbin); } }