Skip to content

Commit 775f75f

Browse files
committed
JAVA-2447: Implement revised extended JSON specification
Support new canonical and relaxed extended JSON forms Change extended JSON parser to throw exceptions for documents that have at least one of the reserved $-prefixed keys but otherwise do not match the extended JSON form
1 parent 30eb7a0 commit 775f75f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+4305
-3794
lines changed

bson/src/main/org/bson/BsonDocument.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,10 +791,14 @@ public int hashCode() {
791791
}
792792

793793
/**
794-
* Gets a JSON representation of this document using the default settings of {@code JsonWriterSettings}.
794+
* Gets a JSON representation of this document using the {@link org.bson.json.JsonMode#STRICT} output mode, and otherwise the default
795+
* settings of {@link JsonWriterSettings.Builder}.
796+
*
795797
* @return a JSON representation of this document
798+
* @see #toJson(JsonWriterSettings)
796799
* @see JsonWriterSettings
797800
*/
801+
@SuppressWarnings("deprecation")
798802
public String toJson() {
799803
return toJson(new JsonWriterSettings());
800804
}

bson/src/main/org/bson/Document.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,13 +261,15 @@ public Date getDate(final Object key) {
261261
}
262262

263263
/**
264-
* Gets a JSON representation of this document
265-
*
266-
* <p>With the default {@link JsonWriterSettings} and {@link DocumentCodec}.</p>
264+
* Gets a JSON representation of this document using the {@link org.bson.json.JsonMode#STRICT} output mode, and otherwise the default
265+
* settings of {@link JsonWriterSettings.Builder} and {@link DocumentCodec}.
267266
*
268267
* @return a JSON representation of this document
269268
* @throws org.bson.codecs.configuration.CodecConfigurationException if the document contains types not in the default registry
269+
* @see #toJson(JsonWriterSettings)
270+
* @see JsonWriterSettings
270271
*/
272+
@SuppressWarnings("deprecation")
271273
public String toJson() {
272274
return toJson(new JsonWriterSettings());
273275
}
@@ -294,6 +296,7 @@ public String toJson(final JsonWriterSettings writerSettings) {
294296
* @return a JSON representation of this document
295297
* @throws org.bson.codecs.configuration.CodecConfigurationException if the registry does not contain a codec for the document values.
296298
*/
299+
@SuppressWarnings("deprecation")
297300
public String toJson(final Encoder<Document> encoder) {
298301
return toJson(new JsonWriterSettings(), encoder);
299302
}

bson/src/main/org/bson/RawBsonDocument.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ public BsonValue get(final Object key) {
294294
}
295295

296296
@Override
297+
@SuppressWarnings("deprecation")
297298
public String toJson() {
298299
return toJson(new JsonWriterSettings());
299300
}

bson/src/main/org/bson/json/DateTimeFormatter.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
import java.lang.reflect.InvocationTargetException;
2121
import java.lang.reflect.Method;
2222
import java.time.Instant;
23+
import java.time.ZoneId;
24+
import java.time.ZonedDateTime;
2325
import java.time.format.DateTimeParseException;
2426
import java.time.temporal.TemporalAccessor;
2527
import java.time.temporal.TemporalQuery;
2628
import java.util.Calendar;
29+
import java.util.TimeZone;
2730

2831
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
2932

@@ -61,19 +64,27 @@ static long parse(final String dateTimeString) {
6164
return FORMATTER_IMPL.parse(dateTimeString);
6265
}
6366

67+
static String format(final long dateTime) {
68+
return FORMATTER_IMPL.format(dateTime);
69+
}
70+
6471
private interface FormatterImpl {
6572
long parse(String dateTimeString);
73+
String format(long dateTime);
6674
}
6775

6876
// Reflective use of DatatypeConverter avoids a compile-time dependency on the java.xml.bind module in Java 9
6977
static class JaxbDateTimeFormatter implements FormatterImpl {
7078

7179
private static final Method DATATYPE_CONVERTER_PARSE_DATE_TIME_METHOD;
80+
private static final Method DATATYPE_CONVERTER_PRINT_DATE_TIME_METHOD;
7281

7382
static {
7483
try {
7584
DATATYPE_CONVERTER_PARSE_DATE_TIME_METHOD = Class.forName("javax.xml.bind.DatatypeConverter")
7685
.getDeclaredMethod("parseDateTime", String.class);
86+
DATATYPE_CONVERTER_PRINT_DATE_TIME_METHOD = Class.forName("javax.xml.bind.DatatypeConverter")
87+
.getDeclaredMethod("printDateTime", Calendar.class);
7788
} catch (NoSuchMethodException e) {
7889
throw new ExceptionInInitializerError(e);
7990
} catch (ClassNotFoundException e) {
@@ -91,6 +102,20 @@ public long parse(final String dateTimeString) {
91102
throw (RuntimeException) e.getCause();
92103
}
93104
}
105+
106+
@Override
107+
public String format(final long dateTime) {
108+
Calendar calendar = Calendar.getInstance();
109+
calendar.setTimeInMillis(dateTime);
110+
calendar.setTimeZone(TimeZone.getTimeZone("Z"));
111+
try {
112+
return (String) DATATYPE_CONVERTER_PRINT_DATE_TIME_METHOD.invoke(null, calendar);
113+
} catch (IllegalAccessException e) {
114+
throw new IllegalStateException();
115+
} catch (InvocationTargetException e) {
116+
throw (RuntimeException) e.getCause();
117+
}
118+
}
94119
}
95120

96121
static class Java8DateTimeFormatter implements FormatterImpl {
@@ -118,6 +143,11 @@ public Instant queryFrom(final TemporalAccessor temporal) {
118143
throw new IllegalArgumentException(e.getMessage());
119144
}
120145
}
146+
147+
@Override
148+
public String format(final long dateTime) {
149+
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(dateTime), ZoneId.of("Z")).format(ISO_OFFSET_DATE_TIME);
150+
}
121151
}
122152

123153
private DateTimeFormatter() {

bson/src/main/org/bson/json/ExtendedJsonBinaryConverter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ class ExtendedJsonBinaryConverter implements Converter<BsonBinary> {
2525
@Override
2626
public void convert(final BsonBinary value, final StrictJsonWriter writer) {
2727
writer.writeStartObject();
28-
writer.writeString("$binary", Base64.encode(value.getData()));
29-
writer.writeString("$type", String.format("%02X", value.getType()));
28+
writer.writeStartObject("$binary");
29+
writer.writeString("base64", Base64.encode(value.getData()));
30+
writer.writeString("subType", String.format("%02X", value.getType()));
31+
writer.writeEndObject();
3032
writer.writeEndObject();
3133
}
3234
}

bson/src/main/org/bson/json/ExtendedJsonRegularExpressionConverter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ class ExtendedJsonRegularExpressionConverter implements Converter<BsonRegularExp
2323
@Override
2424
public void convert(final BsonRegularExpression value, final StrictJsonWriter writer) {
2525
writer.writeStartObject();
26-
writer.writeString("$regex", value.getPattern());
27-
writer.writeString("$options", value.getOptions());
26+
writer.writeStartObject("$regularExpression");
27+
writer.writeString("pattern", value.getPattern());
28+
writer.writeString("options", value.getOptions());
29+
writer.writeEndObject();
2830
writer.writeEndObject();
2931
}
3032
}

bson/src/main/org/bson/json/ExtendedJsonTimestampConverter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ class ExtendedJsonTimestampConverter implements Converter<BsonTimestamp> {
2424
@Override
2525
public void convert(final BsonTimestamp value, final StrictJsonWriter writer) {
2626
writer.writeStartObject();
27-
writer.writeString("$timestamp", UnsignedLongs.toString(value.getValue()));
27+
writer.writeStartObject("$timestamp");
28+
writer.writeNumber("t", UnsignedLongs.toString(toUnsignedLong(value.getTime())));
29+
writer.writeNumber("i", UnsignedLongs.toString(toUnsignedLong(value.getInc())));
2830
writer.writeEndObject();
31+
writer.writeEndObject();
32+
}
33+
34+
// Equivalent to Integer.toUnsignedLong() in Java 8
35+
private long toUnsignedLong(final int value) {
36+
return ((long) value) & 0xffffffffL;
2937
}
3038
}

bson/src/main/org/bson/json/JsonMode.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,13 @@ public enum JsonMode {
4444
* @since 3.5
4545
* @see <a href="https://github.com/mongodb/specifications/blob/master/source/extended-json.rst">Extended JSON Specification</a>
4646
*/
47-
EXTENDED
47+
EXTENDED,
48+
49+
/**
50+
* Standard relaxed extended JSON representation.
51+
*
52+
* @since 3.5
53+
* @see <a href="https://github.com/mongodb/specifications/blob/master/source/extended-json.rst">Extended JSON Specification</a>
54+
*/
55+
RELAXED
4856
}

0 commit comments

Comments
 (0)