1616 */
1717package org .apache .arrow .vector ;
1818
19+ import static org .apache .arrow .vector .testing .ValueVectorDataPopulator .setVector ;
1920import static org .junit .jupiter .api .Assertions .assertEquals ;
2021import static org .junit .jupiter .api .Assertions .assertFalse ;
2122import static org .junit .jupiter .api .Assertions .assertInstanceOf ;
2526
2627import java .io .ByteArrayInputStream ;
2728import java .io .ByteArrayOutputStream ;
29+ import java .io .IOException ;
30+ import java .io .InputStream ;
2831import java .nio .ByteBuffer ;
2932import java .nio .charset .StandardCharsets ;
30- import java .util .Base64 ;
3133import java .util .Collections ;
3234import java .util .stream .Stream ;
35+ import org .apache .arrow .memory .BufferAllocator ;
3336import org .apache .arrow .memory .RootAllocator ;
3437import org .apache .arrow .vector .extension .InvalidExtensionMetadataException ;
3538import org .apache .arrow .vector .extension .JsonType ;
4346import org .apache .arrow .vector .types .pojo .Schema ;
4447import org .apache .arrow .vector .util .Text ;
4548import org .apache .arrow .vector .util .TransferPair ;
49+ import org .junit .jupiter .api .AfterEach ;
50+ import org .junit .jupiter .api .BeforeEach ;
4651import org .junit .jupiter .api .Test ;
4752import org .junit .jupiter .params .ParameterizedTest ;
4853import org .junit .jupiter .params .provider .MethodSource ;
4954import org .junit .jupiter .params .provider .NullSource ;
5055import org .junit .jupiter .params .provider .ValueSource ;
5156
5257class TestJsonType {
58+ BufferAllocator allocator ;
59+ ArrowType .ExtensionType previousType ;
60+
61+ @ BeforeEach
62+ void beforeEach () {
63+ allocator = new RootAllocator ();
64+ previousType = ExtensionTypeRegistry .lookup (JsonType .EXTENSION_NAME );
65+ JsonType .ensureRegistered ();
66+ }
67+
68+ @ AfterEach
69+ void afterEach () {
70+ ExtensionTypeRegistry .unregister (new JsonType (ArrowType .Utf8 .INSTANCE ));
71+ if (previousType != null ) {
72+ ExtensionTypeRegistry .register (previousType );
73+ }
74+ allocator .close ();
75+ }
76+
5377 static Stream <ArrowType > storageTypes () {
5478 return Stream .of (
5579 ArrowType .Utf8 .INSTANCE , ArrowType .LargeUtf8 .INSTANCE , ArrowType .Utf8View .INSTANCE );
5680 }
5781
5882 @ ParameterizedTest
5983 @ MethodSource ("storageTypes" )
60- void testType (ArrowType storage ) {
84+ void testRoundTrip (ArrowType storage ) {
6185 JsonType type = new JsonType (storage );
6286 assertEquals ("arrow.json" , type .extensionName ());
6387 assertEquals (storage , type .storageType ());
6488 assertFalse (type .isComplex ());
6589 assertEquals ("" , type .serialize ());
66- for (String metadata : new String [] {"" , "{}" , " { } " , "{\" future\" : {\" value\" : 1}}" }) {
67- ArrowType restored = type .deserialize (storage , metadata );
68- assertEquals (type , restored );
69- assertEquals (type .hashCode (), restored .hashCode ());
70- }
71- storageTypes ()
72- .filter (other -> !storage .equals (other ))
73- .forEach (other -> assertNotEquals (type , new JsonType (other )));
74- assertNotEquals (type , storage );
90+ assertEquals (type , type .deserialize (storage , type .serialize ()));
91+ assertNotEquals (
92+ type ,
93+ new JsonType (
94+ storage instanceof ArrowType .Utf8
95+ ? ArrowType .LargeUtf8 .INSTANCE
96+ : ArrowType .Utf8 .INSTANCE ));
97+ }
98+
99+ @ ParameterizedTest
100+ @ ValueSource (strings = {"" , "{}" , " { } " , "{\" future\" : 1}" })
101+ void testDeserializeValid (String metadata ) {
102+ JsonType type = new JsonType (ArrowType .Utf8 .INSTANCE );
103+ assertEquals (type , type .deserialize (type .storageType (), metadata ));
75104 }
76105
77106 @ ParameterizedTest
@@ -103,7 +132,6 @@ void testInvalidStorage() {
103132 @ ParameterizedTest
104133 @ MethodSource ("storageTypes" )
105134 void testSchemaRoundTrip (ArrowType storage ) {
106- JsonType .ensureRegistered ();
107135 for (boolean nullable : new boolean [] {false , true }) {
108136 Field field = field (storage , nullable );
109137 Schema schema = new Schema (Collections .singletonList (field ));
@@ -115,21 +143,11 @@ void testSchemaRoundTrip(ArrowType storage) {
115143 // nullable=False, metadata={"custom": "preserved"}) for t in
116144 // [pa.string(), pa.large_string(), pa.string_view()]]).serialize().
117145 @ Test
118- void testPyArrowSchema () {
119- JsonType .ensureRegistered ();
120- String encoded =
121- "/////9ACAAAQAAAAAAAKAAwABgAFAAgACgAAAAABBAAMAAAACAAIAAAABAAIAAAABAAAAAMAAADEAQAA2AAAAAQAAABa/v//"
122- + "AAAAGBQAAADAAAAACAAAABQAAAAAAAAABAAAAGpzb24AAAAAAwAAAHgAAABAAAAABAAAANz9//8YAAAABAAAAAoAAABhcnJv"
123- + "dy5qc29uAAAUAAAAQVJST1c6ZXh0ZW5zaW9uOm5hbWUAAAAAFP7//xAAAAAEAAAAAAAAAAAAAAAYAAAAQVJST1c6ZXh0ZW5z"
124- + "aW9uOm1ldGFkYXRhAAAAAEj+//8YAAAABAAAAAkAAABwcmVzZXJ2ZWQAAAAGAAAAY3VzdG9tAABA/v//Kv///wAAABQUAAAA"
125- + "xAAAAAgAAAAUAAAAAAAAAAQAAABqc29uAAAAAAMAAAB4AAAAQAAAAAQAAACs/v//GAAAAAQAAAAKAAAAYXJyb3cuanNvbgAA"
126- + "FAAAAEFSUk9XOmV4dGVuc2lvbjpuYW1lAAAAAOT+//8QAAAABAAAAAAAAAAAAAAAGAAAAEFSUk9XOmV4dGVuc2lvbjptZXRh"
127- + "ZGF0YQAAAAAY////GAAAAAQAAAAJAAAAcHJlc2VydmVkAAAABgAAAGN1c3RvbQAABAAGAAQAAAAAABIAGAAIAAAABwAMAAAA"
128- + "EAAUABIAAAAAAAAFFAAAAMwAAAAIAAAAFAAAAAAAAAAEAAAAanNvbgAAAAADAAAAgAAAAEAAAAAEAAAAlP///xgAAAAEAAAA"
129- + "CgAAAGFycm93Lmpzb24AABQAAABBUlJPVzpleHRlbnNpb246bmFtZQAAAADM////EAAAAAQAAAAAAAAAAAAAABgAAABBUlJP"
130- + "VzpleHRlbnNpb246bWV0YWRhdGEAAAAACAAMAAQACAAIAAAAGAAAAAQAAAAJAAAAcHJlc2VydmVkAAAABgAAAGN1c3RvbQAA"
131- + "BAAEAAQAAAA=" ;
132- Schema schema = Schema .deserializeMessage (ByteBuffer .wrap (Base64 .getDecoder ().decode (encoded )));
146+ void testPyArrowSchema () throws IOException {
147+ Schema schema ;
148+ try (InputStream input = getClass ().getResourceAsStream ("/pyarrow_json_schema.arrow" )) {
149+ schema = Schema .deserializeMessage (ByteBuffer .wrap (input .readAllBytes ()));
150+ }
133151 ArrowType [] storage = storageTypes ().toArray (ArrowType []::new );
134152 for (int i = 0 ; i < storage .length ; i ++) {
135153 assertEquals (field (storage [i ], false ), schema .getFields ().get (i ));
@@ -148,108 +166,95 @@ nullable, new JsonType(storage), null, Collections.singletonMap("custom", "prese
148166 @ MethodSource ("storageTypes" )
149167 void testTransfer (ArrowType storage ) {
150168 Field field = field (storage , true );
151- try (RootAllocator allocator = new RootAllocator ();
152- JsonVector source = (JsonVector ) field .createVector (allocator )) {
169+ try (JsonVector source = (JsonVector ) field .createVector (allocator )) {
153170 byte [] bytes =
154171 "{\" key\" :\" value longer than twelve bytes\" }" .getBytes (StandardCharsets .UTF_8 );
155- FieldVector underlying = source .getUnderlyingVector ();
156- if (underlying instanceof VarCharVector ) {
157- ((VarCharVector ) underlying ).setSafe (0 , bytes );
158- } else if (underlying instanceof LargeVarCharVector ) {
159- ((LargeVarCharVector ) underlying ).setSafe (0 , bytes );
160- } else {
161- ((ViewVarCharVector ) underlying ).setSafe (0 , bytes );
162- }
163- source .setNull (1 );
164- source .setValueCount (2 );
172+ setVector ((VariableWidthFieldVector ) source .getUnderlyingVector (), null , bytes , null );
165173 TransferPair split = source .getTransferPair ("copy" , allocator );
166174 try (JsonVector target = assertInstanceOf (JsonVector .class , split .getTo ())) {
167- split .splitAndTransfer (0 , 2 );
175+ split .splitAndTransfer (1 , 2 );
168176 assertEquals ("copy" , target .getName ());
169177 assertEquals (field .getFieldType (), target .getField ().getFieldType ());
170- assertEquals (source . getObject ( 0 ), target .getObject (0 ));
178+ assertEquals (new Text ( bytes ), target .getObject (0 ));
171179 assertTrue (target .isNull (1 ));
172180 }
173181 try (JsonVector target = (JsonVector ) field .createVector (allocator )) {
174182 TransferPair copy = source .makeTransferPair (target );
175- copy .copyValueSafe (0 , 0 );
176- copy .copyValueSafe (1 , 1 );
183+ copy .copyValueSafe (1 , 0 );
184+ copy .copyValueSafe (2 , 1 );
177185 target .setValueCount (2 );
178- assertEquals (source . getObject ( 0 ), target .getObject (0 ));
186+ assertEquals (new Text ( bytes ), target .getObject (0 ));
179187 assertTrue (target .isNull (1 ));
180188 }
181189 TransferPair transfer = source .getTransferPair (allocator );
182190 try (JsonVector target = assertInstanceOf (JsonVector .class , transfer .getTo ())) {
183191 transfer .transfer ();
184192 assertEquals (field , target .getField ());
185- assertEquals (new Text (bytes ), target .getObject (0 ));
186- assertTrue (target .isNull (1 ));
187- assertEquals (2 , target .getValueCount ());
193+ assertTrue (target .isNull (0 ));
194+ assertEquals (new Text (bytes ), target .getObject (1 ));
195+ assertTrue (target .isNull (2 ));
196+ assertEquals (3 , target .getValueCount ());
188197 assertEquals (0 , source .getValueCount ());
189198 }
190199 }
191200 }
192201
193202 @ ParameterizedTest
194203 @ MethodSource ("storageTypes" )
195- void testIpcAndUnregisteredFallback (ArrowType storage ) throws Exception {
196- ArrowType .ExtensionType previous = ExtensionTypeRegistry .lookup (JsonType .EXTENSION_NAME );
197- JsonType type = new JsonType (storage );
198- JsonType .ensureRegistered ();
199- try (RootAllocator allocator = new RootAllocator ()) {
200- Field field = field (storage , true );
201- Schema schema = new Schema (Collections .singletonList (field ));
202- ByteArrayOutputStream out = new ByteArrayOutputStream ();
203- String [] values = {
204- "{\" message\" :\" 你好, a JSON value longer than twelve bytes\" }" , null , "null" , "42" , "[]"
205- };
206- try (VectorSchemaRoot root = VectorSchemaRoot .create (schema , allocator );
207- ArrowStreamWriter writer = new ArrowStreamWriter (root , null , out )) {
208- JsonVector vector = assertInstanceOf (JsonVector .class , root .getVector (0 ));
209- FieldVector underlying = vector .getUnderlyingVector ();
210- for (int i = 0 ; i < values .length ; i ++) {
211- if (values [i ] == null ) {
212- vector .setNull (i );
213- } else {
214- byte [] bytes = values [i ].getBytes (StandardCharsets .UTF_8 );
215- if (storage instanceof ArrowType .Utf8 ) {
216- assertInstanceOf (VarCharVector .class , underlying ).setSafe (i , bytes );
217- } else if (storage instanceof ArrowType .LargeUtf8 ) {
218- assertInstanceOf (LargeVarCharVector .class , underlying ).setSafe (i , bytes );
219- } else {
220- assertInstanceOf (ViewVarCharVector .class , underlying ).setSafe (i , bytes );
221- }
222- }
223- }
224- root .setRowCount (values .length );
225- assertEquals (underlying .hashCode (0 ), vector .hashCode (0 ));
226- writer .start ();
227- writer .writeBatch ();
228- writer .end ();
229- }
230- for (boolean registered : new boolean [] {true , false }) {
231- if (!registered ) {
232- ExtensionTypeRegistry .unregister (type );
233- }
234- try (ArrowStreamReader reader =
235- new ArrowStreamReader (new ByteArrayInputStream (out .toByteArray ()), allocator )) {
236- assertTrue (reader .loadNextBatch ());
237- FieldVector vector = reader .getVectorSchemaRoot ().getVector (0 );
238- assertEquals (registered ? type : storage , vector .getField ().getType ());
239- assertEquals (field .getMetadata (), vector .getField ().getMetadata ());
240- assertTrue (vector .getField ().isNullable ());
241- assertEquals (values .length , vector .getValueCount ());
242- for (int i = 0 ; i < values .length ; i ++) {
243- assertEquals (values [i ] == null ? null : new Text (values [i ]), vector .getObject (i ));
244- }
245- assertFalse (reader .loadNextBatch ());
246- }
247- }
248- } finally {
249- ExtensionTypeRegistry .unregister (type );
250- if (previous != null ) {
251- ExtensionTypeRegistry .register (previous );
252- }
204+ void testVectorIpcRoundTrip (ArrowType storage ) throws IOException {
205+ Field field = field (storage , true );
206+ byte [] serialized = writeStream (field );
207+ try (ArrowStreamReader reader =
208+ new ArrowStreamReader (new ByteArrayInputStream (serialized ), allocator )) {
209+ assertTrue (reader .loadNextBatch ());
210+ JsonVector vector =
211+ assertInstanceOf (JsonVector .class , reader .getVectorSchemaRoot ().getVector (0 ));
212+ assertEquals (field , vector .getField ());
213+ assertValues (vector );
253214 }
254215 }
216+
217+ @ ParameterizedTest
218+ @ MethodSource ("storageTypes" )
219+ void testReadUnderlyingType (ArrowType storage ) throws IOException {
220+ Field field = field (storage , true );
221+ byte [] serialized = writeStream (field );
222+ ExtensionTypeRegistry .unregister ((JsonType ) field .getType ());
223+ try (ArrowStreamReader reader =
224+ new ArrowStreamReader (new ByteArrayInputStream (serialized ), allocator )) {
225+ assertTrue (reader .loadNextBatch ());
226+ FieldVector vector = reader .getVectorSchemaRoot ().getVector (0 );
227+ assertEquals (storage , vector .getField ().getType ());
228+ assertEquals (field .getMetadata (), vector .getField ().getMetadata ());
229+ assertValues (vector );
230+ }
231+ }
232+
233+ private static final String JSON = "{\" message\" :\" 你好, a JSON value longer than twelve bytes\" }" ;
234+
235+ private byte [] writeStream (Field field ) throws IOException {
236+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
237+ try (VectorSchemaRoot root =
238+ VectorSchemaRoot .create (new Schema (Collections .singletonList (field )), allocator );
239+ ArrowStreamWriter writer = new ArrowStreamWriter (root , null , out )) {
240+ JsonVector vector = (JsonVector ) root .getVector (0 );
241+ setVector (
242+ (VariableWidthFieldVector ) vector .getUnderlyingVector (),
243+ JSON .getBytes (StandardCharsets .UTF_8 ),
244+ null ,
245+ "null" .getBytes (StandardCharsets .UTF_8 ));
246+ root .setRowCount (3 );
247+ writer .start ();
248+ writer .writeBatch ();
249+ writer .end ();
250+ }
251+ return out .toByteArray ();
252+ }
253+
254+ private static void assertValues (FieldVector vector ) {
255+ assertEquals (3 , vector .getValueCount ());
256+ assertEquals (new Text (JSON ), vector .getObject (0 ));
257+ assertTrue (vector .isNull (1 ));
258+ assertEquals (new Text ("null" ), vector .getObject (2 ));
259+ }
255260}
0 commit comments