1313 */
1414package com .facebook .presto .util ;
1515
16+ import com .facebook .airlift .json .JsonObjectMapperProvider ;
1617import com .facebook .presto .common .block .Block ;
1718import com .facebook .presto .common .block .BlockBuilder ;
1819import com .facebook .presto .common .block .SingleRowBlockWriter ;
4142import com .fasterxml .jackson .databind .ObjectMapper ;
4243import com .google .common .primitives .Shorts ;
4344import com .google .common .primitives .SignedBytes ;
45+ import io .airlift .slice .DynamicSliceOutput ;
4446import io .airlift .slice .Slice ;
4547import io .airlift .slice .SliceOutput ;
4648import io .airlift .slice .Slices ;
8688import static com .fasterxml .jackson .core .JsonToken .FIELD_NAME ;
8789import static com .fasterxml .jackson .core .JsonToken .START_ARRAY ;
8890import static com .fasterxml .jackson .core .JsonToken .START_OBJECT ;
91+ import static com .fasterxml .jackson .databind .SerializationFeature .ORDER_MAP_ENTRIES_BY_KEYS ;
8992import static com .google .common .base .Preconditions .checkArgument ;
9093import static com .google .common .base .Verify .verify ;
9194import static it .unimi .dsi .fastutil .HashCommon .arraySize ;
@@ -106,6 +109,7 @@ public final class JsonUtil
106109 // `OBJECT_MAPPER.writeValueAsString(parser.readValueAsTree());` preserves input order.
107110 // Be aware. Using it arbitrarily can produce invalid json (ordered by key is required in Presto).
108111 private static final ObjectMapper OBJECT_MAPPED_UNORDERED = new ObjectMapper (JSON_FACTORY );
112+ private static final ObjectMapper OBJECT_MAPPED_SORTED = new JsonObjectMapperProvider ().get ().configure (ORDER_MAP_ENTRIES_BY_KEYS , true );
109113
110114 private static final int MAX_JSON_LENGTH_IN_ERROR_MESSAGE = 10_000 ;
111115
@@ -956,8 +960,18 @@ static BlockBuilderAppender createBlockBuilderAppender(Type type)
956960 return new VarcharBlockBuilderAppender (type );
957961 case StandardTypes .JSON :
958962 return (parser , blockBuilder , sqlFunctionProperties ) -> {
959- String json = OBJECT_MAPPED_UNORDERED .writeValueAsString (parser .readValueAsTree ());
960- JSON .writeSlice (blockBuilder , Slices .utf8Slice (json ));
963+ Slice slice = Slices .utf8Slice (OBJECT_MAPPED_UNORDERED .writeValueAsString (parser .readValueAsTree ()));
964+ try (JsonParser jsonParser = createJsonParser (JSON_FACTORY , slice )) {
965+ SliceOutput dynamicSliceOutput = new DynamicSliceOutput (slice .length ());
966+ OBJECT_MAPPED_SORTED .writeValue ((OutputStream ) dynamicSliceOutput , OBJECT_MAPPED_SORTED .readValue (jsonParser , Object .class ));
967+ // nextToken() returns null if the input is parsed correctly,
968+ // but will throw an exception if there are trailing characters.
969+ jsonParser .nextToken ();
970+ JSON .writeSlice (blockBuilder , dynamicSliceOutput .slice ());
971+ }
972+ catch (Exception e ) {
973+ throw new PrestoException (INVALID_FUNCTION_ARGUMENT , format ("Cannot convert '%s' to JSON" , slice .toStringUtf8 ()));
974+ }
961975 };
962976 case StandardTypes .ARRAY :
963977 return new ArrayBlockBuilderAppender (createBlockBuilderAppender (((ArrayType ) type ).getElementType ()));
0 commit comments