Skip to main content

parquet_variant_compute/
variant_array_builder.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! [`VariantArrayBuilder`] implementation
19
20use crate::VariantArray;
21use arrow::array::builder::make_view;
22use arrow::array::{ArrayRef, BinaryViewArray, NullBufferBuilder, StructArray};
23use arrow::buffer::Buffer;
24use arrow_schema::{ArrowError, DataType, Field, Fields};
25use parquet_variant::{
26    BuilderSpecificState, ListBuilder, MetadataBuilder, ObjectBuilder, Variant, VariantBuilderExt,
27    VariantMetadata,
28};
29use parquet_variant::{
30    ParentState, ReadOnlyMetadataBuilder, ValueBuilder, WritableMetadataBuilder,
31};
32use std::sync::Arc;
33
34/// A builder for [`VariantArray`]
35///
36/// This builder is used to construct a `VariantArray` and allows APIs for
37/// adding metadata
38///
39/// This builder always creates a `VariantArray` using [`BinaryViewArray`] for both
40/// the metadata and value fields.
41///
42/// `VariantArrayBuilder` implements [`VariantBuilderExt`], so you append values
43/// and nested objects or lists the same way as when building a single
44/// [`Variant`] value with [`VariantBuilder`], rather than constructing a
45/// `VariantBuilder` per row.
46///
47/// [`VariantBuilder`]: parquet_variant::VariantBuilder
48/// [`VariantBuilderExt`]: parquet_variant::VariantBuilderExt
49///
50/// # TODO
51/// 1. Support shredding: <https://github.com/apache/arrow-rs/issues/7895>
52///
53/// ## Example:
54/// ```
55/// # use arrow::array::Array;
56/// # use parquet_variant::{Variant, VariantBuilder, VariantBuilderExt};
57/// # use parquet_variant_compute::VariantArrayBuilder;
58/// # use parquet_variant::ShortString;
59/// // Create a new VariantArrayBuilder with a capacity of 100 rows
60/// let mut builder = VariantArrayBuilder::new(100);
61/// // append variant values
62/// builder.append_variant(Variant::from(42));
63/// // append a null row (note not a Variant::Null)
64/// builder.append_null();
65/// // append an object to the builder using VariantBuilderExt methods directly
66/// builder.new_object()
67///   .with_field("foo", "bar")
68///   .finish();
69///
70/// // bulk insert a list of values
71/// // `Option::None` is a null value
72/// builder.extend([None, Some(Variant::from("norm"))]);
73///
74/// // create the final VariantArray
75/// let variant_array = builder.build();
76/// assert_eq!(variant_array.len(), 5);
77/// // // Access the values
78/// // row 1 is not null and is an integer
79/// assert!(!variant_array.is_null(0));
80/// assert_eq!(variant_array.value(0), Variant::from(42i32));
81/// // row 1 is null
82/// assert!(variant_array.is_null(1));
83/// // row 2 is not null and is an object
84/// assert!(!variant_array.is_null(2));
85/// let value = variant_array.value(2);
86/// let obj = value.as_object().expect("expected object");
87/// assert_eq!(obj.get("foo"), Some(Variant::from("bar")));
88/// // row 3 is null
89/// assert!(variant_array.is_null(3));
90/// // row 4 is not null and is a short string
91/// assert!(!variant_array.is_null(4));
92/// let value = variant_array.value(4);
93/// assert_eq!(value, Variant::ShortString(ShortString::try_new("norm").unwrap()));
94/// ```
95#[derive(Debug)]
96pub struct VariantArrayBuilder {
97    /// Nulls
98    nulls: NullBufferBuilder,
99    /// builder for all the metadata
100    metadata_builder: WritableMetadataBuilder,
101    /// ending offset for each serialized metadata dictionary in the buffer
102    metadata_offsets: Vec<usize>,
103    /// builder for values
104    value_builder: ValueBuilder,
105    /// ending offset for each serialized variant value in the buffer
106    value_offsets: Vec<usize>,
107    /// The fields of the final `StructArray`
108    ///
109    /// TODO: 1) Add extension type metadata
110    /// TODO: 2) Add support for shredding
111    fields: Fields,
112}
113
114impl VariantArrayBuilder {
115    pub fn new(row_capacity: usize) -> Self {
116        // The subfields are expected to be non-nullable according to the parquet variant spec.
117        let metadata_field = Field::new("metadata", DataType::BinaryView, false);
118        let value_field = Field::new("value", DataType::BinaryView, false);
119
120        Self {
121            nulls: NullBufferBuilder::new(row_capacity),
122            metadata_builder: WritableMetadataBuilder::default(),
123            metadata_offsets: Vec::with_capacity(row_capacity),
124            value_builder: ValueBuilder::new(),
125            value_offsets: Vec::with_capacity(row_capacity),
126            fields: Fields::from(vec![metadata_field, value_field]),
127        }
128    }
129
130    /// Build the final builder
131    pub fn build(self) -> VariantArray {
132        let Self {
133            mut nulls,
134            metadata_builder,
135            metadata_offsets,
136            value_builder,
137            value_offsets,
138            fields,
139        } = self;
140
141        let metadata_buffer = metadata_builder.into_inner();
142        let metadata_array = binary_view_array_from_buffers(metadata_buffer, metadata_offsets);
143
144        let value_buffer = value_builder.into_inner();
145        let value_array = binary_view_array_from_buffers(value_buffer, value_offsets);
146
147        // The build the final struct array
148        let inner = StructArray::new(
149            fields,
150            vec![
151                Arc::new(metadata_array) as ArrayRef,
152                Arc::new(value_array) as ArrayRef,
153            ],
154            nulls.finish(),
155        );
156        // TODO add arrow extension type metadata
157
158        VariantArray::try_new(&inner).expect("valid VariantArray by construction")
159    }
160
161    /// Appends a null row to the builder.
162    pub fn append_null(&mut self) {
163        self.nulls.append_null();
164        // The subfields are expected to be non-nullable according to the parquet variant spec.
165        self.metadata_offsets.push(self.metadata_builder.offset());
166        self.value_offsets.push(self.value_builder.offset());
167    }
168
169    /// Appends `n` null rows to the builder.
170    pub fn append_nulls(&mut self, n: usize) {
171        self.nulls.append_n_nulls(n);
172        // The subfields are expected to be non-nullable according to the parquet variant spec.
173        let metadata_offset = self.metadata_builder.offset();
174        let value_offset = self.value_builder.offset();
175        self.metadata_offsets
176            .extend(std::iter::repeat_n(metadata_offset, n));
177        self.value_offsets
178            .extend(std::iter::repeat_n(value_offset, n));
179    }
180
181    /// Append the [`Variant`] to the builder as the next row
182    pub fn append_variant(&mut self, variant: Variant) {
183        ValueBuilder::append_variant(self.parent_state(), variant);
184    }
185
186    /// Creates a builder-specific parent state
187    fn parent_state(&mut self) -> ParentState<'_, ArrayBuilderState<'_>> {
188        let state = ArrayBuilderState {
189            metadata_offsets: &mut self.metadata_offsets,
190            value_offsets: &mut self.value_offsets,
191            nulls: &mut self.nulls,
192        };
193
194        ParentState::new(&mut self.value_builder, &mut self.metadata_builder, state)
195    }
196}
197
198impl<'m, 'v> Extend<Option<Variant<'m, 'v>>> for VariantArrayBuilder {
199    fn extend<T: IntoIterator<Item = Option<Variant<'m, 'v>>>>(&mut self, iter: T) {
200        for v in iter {
201            match v {
202                Some(v) => self.append_variant(v),
203                None => self.append_null(),
204            }
205        }
206    }
207}
208
209/// Builder-specific state for array building that manages array-level offsets and nulls. See
210/// [`VariantBuilderExt`] for details.
211#[derive(Debug)]
212pub struct ArrayBuilderState<'a> {
213    metadata_offsets: &'a mut Vec<usize>,
214    value_offsets: &'a mut Vec<usize>,
215    nulls: &'a mut NullBufferBuilder,
216}
217
218// All changes are pending until finalized
219impl BuilderSpecificState for ArrayBuilderState<'_> {
220    fn finish(
221        &mut self,
222        metadata_builder: &mut dyn MetadataBuilder,
223        value_builder: &mut ValueBuilder,
224    ) {
225        self.metadata_offsets.push(metadata_builder.finish());
226        self.value_offsets.push(value_builder.offset());
227        self.nulls.append_non_null();
228    }
229}
230
231impl VariantBuilderExt for VariantArrayBuilder {
232    type State<'a>
233        = ArrayBuilderState<'a>
234    where
235        Self: 'a;
236
237    /// Appending NULL to a variant array produces an actual NULL value
238    fn append_null(&mut self) {
239        self.append_null();
240    }
241
242    fn append_value<'m, 'v>(&mut self, value: impl Into<Variant<'m, 'v>>) {
243        self.append_variant(value.into());
244    }
245
246    fn try_new_list(&mut self) -> Result<ListBuilder<'_, Self::State<'_>>, ArrowError> {
247        Ok(ListBuilder::new(self.parent_state(), false))
248    }
249
250    fn try_new_object(&mut self) -> Result<ObjectBuilder<'_, Self::State<'_>>, ArrowError> {
251        Ok(ObjectBuilder::new(self.parent_state(), false))
252    }
253}
254
255/// A builder for creating only the value column of a [`VariantArray`]
256///
257/// This builder is used when you have existing metadata and only need to build
258/// the value column. It's useful for scenarios like variant unshredding, data
259/// transformation, or filtering where you want to reuse existing metadata.
260///
261/// The builder produces a [`BinaryViewArray`] that can be combined with existing
262/// metadata to create a complete [`VariantArray`].
263///
264/// # Example:
265/// ```
266/// # use arrow::array::Array;
267/// # use parquet_variant::{Variant};
268/// # use parquet_variant_compute::VariantValueArrayBuilder;
269/// // Create a variant value builder for 10 rows
270/// let mut builder = VariantValueArrayBuilder::new(10);
271///
272/// // Append some values with their corresponding metadata, which the
273/// // builder takes advantage of to avoid creating new metadata.
274/// builder.append_value(Variant::from(42));
275/// builder.append_null();
276/// builder.append_value(Variant::from("hello"));
277///
278/// // Build the final value array
279/// let value_array = builder.build().unwrap();
280/// assert_eq!(value_array.len(), 3);
281/// ```
282#[derive(Debug)]
283pub struct VariantValueArrayBuilder {
284    value_builder: ValueBuilder,
285    value_offsets: Vec<usize>,
286    nulls: NullBufferBuilder,
287}
288
289impl VariantValueArrayBuilder {
290    /// Create a new `VariantValueArrayBuilder` with the specified row capacity
291    pub fn new(row_capacity: usize) -> Self {
292        Self {
293            value_builder: ValueBuilder::new(),
294            value_offsets: Vec::with_capacity(row_capacity),
295            nulls: NullBufferBuilder::new(row_capacity),
296        }
297    }
298
299    /// Build the final value array
300    ///
301    /// Returns a [`BinaryViewArray`] containing the serialized variant values.
302    /// This can be combined with existing metadata to create a complete [`VariantArray`].
303    pub fn build(mut self) -> Result<BinaryViewArray, ArrowError> {
304        let value_buffer = self.value_builder.into_inner();
305        let mut array = binary_view_array_from_buffers(value_buffer, self.value_offsets);
306        if let Some(nulls) = self.nulls.finish() {
307            let (views, buffers, _) = array.into_parts();
308            array = BinaryViewArray::try_new(views, buffers, Some(nulls))?;
309        }
310        Ok(array)
311    }
312
313    /// Append a null row to the builder
314    ///
315    /// WARNING: It is only valid to call this method when building the `value` field of a shredded
316    /// variant column (which is nullable). The `value` field of a binary (unshredded) variant
317    /// column is non-nullable, and callers should instead invoke [`Self::append_value`] with
318    /// `Variant::Null`, passing the appropriate metadata value.
319    pub fn append_null(&mut self) {
320        self.value_offsets.push(self.value_builder.offset());
321        self.nulls.append_null();
322    }
323
324    /// Append a variant value with its corresponding metadata
325    ///
326    /// # Arguments
327    /// * `value` - The variant value to append
328    /// * `metadata` - The metadata dictionary for this variant (used for field name resolution)
329    ///
330    /// # Returns
331    /// * `Ok(())` if the value was successfully appended
332    /// * `Err(ArrowError)` if the variant contains field names not found in the metadata
333    ///
334    /// # Example
335    /// ```
336    /// # use parquet_variant::Variant;
337    /// # use parquet_variant_compute::VariantValueArrayBuilder;
338    /// let mut builder = VariantValueArrayBuilder::new(10);
339    /// builder.append_value(Variant::from(42));
340    /// ```
341    pub fn append_value(&mut self, value: Variant<'_, '_>) {
342        // NOTE: Have to clone because the builder consumes `value`
343        self.builder_ext(&value.metadata().clone())
344            .append_value(value);
345    }
346
347    /// Creates a builder-specific parent state.
348    ///
349    /// For example, this can be useful for code that wants to copy a subset of fields from an
350    /// object `value` as a new row of `value_array_builder`:
351    ///
352    /// ```no_run
353    /// # use parquet_variant::{ObjectBuilder, ReadOnlyMetadataBuilder, Variant};
354    /// # use parquet_variant_compute::VariantValueArrayBuilder;
355    /// # let value = Variant::Null;
356    /// # let mut value_array_builder = VariantValueArrayBuilder::new(0);
357    /// # fn should_keep(field_name: &str) -> bool { todo!() };
358    /// let Variant::Object(obj) = value else {
359    ///     panic!("Not a variant object");
360    /// };
361    /// let mut metadata_builder = ReadOnlyMetadataBuilder::new(&obj.metadata);
362    /// let state = value_array_builder.parent_state(&mut metadata_builder);
363    /// let mut object_builder = ObjectBuilder::new(state, false);
364    /// for (field_name, field_value) in obj.iter() {
365    ///     if should_keep(field_name) {
366    ///         object_builder.insert_bytes(field_name, field_value);
367    ///     }
368    /// }
369    ///  object_builder.finish(); // appends the filtered object
370    /// ```
371    pub fn parent_state<'a>(
372        &'a mut self,
373        metadata_builder: &'a mut dyn MetadataBuilder,
374    ) -> ParentState<'a, ValueArrayBuilderState<'a>> {
375        let state = ValueArrayBuilderState {
376            value_offsets: &mut self.value_offsets,
377            nulls: &mut self.nulls,
378        };
379
380        ParentState::new(&mut self.value_builder, metadata_builder, state)
381    }
382
383    /// Creates a thin [`VariantBuilderExt`] wrapper for this builder, which hides the `metadata`
384    /// parameter (similar to the way [`parquet_variant::ObjectFieldBuilder`] hides field names).
385    pub fn builder_ext<'a>(
386        &'a mut self,
387        metadata: &'a VariantMetadata<'a>,
388    ) -> VariantValueArrayBuilderExt<'a> {
389        VariantValueArrayBuilderExt {
390            metadata_builder: ReadOnlyMetadataBuilder::new(metadata),
391            value_builder: self,
392        }
393    }
394}
395
396/// Builder-specific state for array building that manages array-level offsets and nulls. See
397/// [`VariantBuilderExt`] for details.
398#[derive(Debug)]
399pub struct ValueArrayBuilderState<'a> {
400    value_offsets: &'a mut Vec<usize>,
401    nulls: &'a mut NullBufferBuilder,
402}
403
404// All changes are pending until finalized
405impl BuilderSpecificState for ValueArrayBuilderState<'_> {
406    fn finish(
407        &mut self,
408        _metadata_builder: &mut dyn MetadataBuilder,
409        value_builder: &mut ValueBuilder,
410    ) {
411        self.value_offsets.push(value_builder.offset());
412        self.nulls.append_non_null();
413    }
414}
415
416/// A thin [`VariantBuilderExt`] wrapper that hides the short-lived (per-row)
417/// [`ReadOnlyMetadataBuilder`] instances that [`VariantValueArrayBuilder`] requires.
418pub struct VariantValueArrayBuilderExt<'a> {
419    metadata_builder: ReadOnlyMetadataBuilder<'a>,
420    value_builder: &'a mut VariantValueArrayBuilder,
421}
422
423impl<'a> VariantValueArrayBuilderExt<'a> {
424    /// Creates a new instance from a metadata builder and a reference to a variant value builder.
425    pub fn new(
426        metadata_builder: ReadOnlyMetadataBuilder<'a>,
427        value_builder: &'a mut VariantValueArrayBuilder,
428    ) -> Self {
429        Self {
430            metadata_builder,
431            value_builder,
432        }
433    }
434}
435
436impl VariantBuilderExt for VariantValueArrayBuilderExt<'_> {
437    type State<'b>
438        = ValueArrayBuilderState<'b>
439    where
440        Self: 'b;
441
442    fn append_null(&mut self) {
443        self.value_builder.append_null()
444    }
445
446    fn append_value<'m, 'v>(&mut self, value: impl Into<Variant<'m, 'v>>) {
447        let state = self.value_builder.parent_state(&mut self.metadata_builder);
448        ValueBuilder::append_variant_bytes(state, value.into());
449    }
450
451    fn try_new_list(&mut self) -> Result<ListBuilder<'_, Self::State<'_>>, ArrowError> {
452        let state = self.value_builder.parent_state(&mut self.metadata_builder);
453        Ok(ListBuilder::new(state, false))
454    }
455
456    fn try_new_object(&mut self) -> Result<ObjectBuilder<'_, Self::State<'_>>, ArrowError> {
457        let state = self.value_builder.parent_state(&mut self.metadata_builder);
458        Ok(ObjectBuilder::new(state, false))
459    }
460}
461
462fn binary_view_array_from_buffers(buffer: Vec<u8>, offsets: Vec<usize>) -> BinaryViewArray {
463    // Each builder records the current buffer length after appending a row, so offsets are
464    // monotonically increasing and bounded by the final buffer length.
465    assert!(buffer.len() < u32::MAX as usize);
466
467    let buffer = Buffer::from(buffer);
468    let mut views = Vec::with_capacity(offsets.len());
469    let mut start = 0_usize;
470    for end in offsets {
471        views.push(make_view(&buffer[start..end], 0, start as u32));
472        start = end;
473    }
474
475    // SAFETY: `make_view` constructs every view from an in-bounds slice of buffer 0, and there
476    // are no nulls. The buffer length check above guarantees every offset fits in a `u32`.
477    unsafe { BinaryViewArray::new_unchecked(views.into(), vec![buffer].into(), None) }
478}
479
480#[cfg(test)]
481mod test {
482    use super::*;
483    use arrow::array::Array;
484    use parquet_variant::{ShortString, Variant};
485
486    /// Test that both the metadata and value buffers are non nullable
487    #[test]
488    fn test_variant_array_builder_non_nullable() {
489        let mut builder = VariantArrayBuilder::new(10);
490
491        builder.extend([
492            None, // should not panic
493            Some(Variant::from(42_i32)),
494        ]);
495
496        let variant_array = builder.build();
497
498        assert_eq!(variant_array.len(), 2);
499        assert!(variant_array.is_null(0));
500        assert!(!variant_array.is_null(1));
501        assert_eq!(variant_array.value(1), Variant::from(42i32));
502
503        // the metadata and value fields of non shredded variants should not be null
504        assert!(variant_array.metadata_column().nulls().is_none());
505        assert!(variant_array.value_column().nulls().is_none());
506        let DataType::Struct(fields) = variant_array.data_type() else {
507            panic!("Expected VariantArray to have Struct data type");
508        };
509        for field in fields {
510            assert!(
511                !field.is_nullable(),
512                "Field {} should be non-nullable",
513                field.name()
514            );
515        }
516    }
517
518    /// Test using appending variants to the array builder
519    #[test]
520    fn test_variant_array_builder() {
521        let mut builder = VariantArrayBuilder::new(10);
522        builder.append_null(); // should not panic
523        builder.append_variant(Variant::from(42i32));
524
525        // make an object in the next row
526        builder.new_object().with_field("foo", "bar").finish();
527
528        // append a new list
529        builder
530            .new_list()
531            .with_value(Variant::from(1i32))
532            .with_value(Variant::from(2i32))
533            .finish();
534        let variant_array = builder.build();
535
536        assert_eq!(variant_array.len(), 4);
537        assert!(variant_array.is_null(0));
538        assert!(!variant_array.is_null(1));
539        assert_eq!(variant_array.value(1), Variant::from(42i32));
540        assert!(!variant_array.is_null(2));
541        let variant = variant_array.value(2);
542        let variant = variant.as_object().expect("variant to be an object");
543        assert_eq!(variant.get("foo").unwrap(), Variant::from("bar"));
544        assert!(!variant_array.is_null(3));
545        let variant = variant_array.value(3);
546        let list = variant.as_list().expect("variant to be a list");
547        assert_eq!(list.len(), 2);
548    }
549
550    #[test]
551    fn test_variant_array_builder_append_nulls() {
552        let mut builder = VariantArrayBuilder::new(6);
553        builder.append_variant(Variant::from(1i32));
554        builder.append_nulls(0); // should be a no-op
555        builder.append_nulls(3);
556        builder.append_variant(Variant::from(2i32));
557
558        let variant_array = builder.build();
559
560        assert_eq!(variant_array.len(), 5);
561        assert_eq!(variant_array.value(0), Variant::from(1i32));
562        assert!(variant_array.is_null(1));
563        assert!(variant_array.is_null(2));
564        assert!(variant_array.is_null(3));
565        assert_eq!(variant_array.value(4), Variant::from(2i32));
566    }
567
568    #[test]
569    fn test_extend_variant_array_builder() {
570        let mut b = VariantArrayBuilder::new(3);
571        b.extend([None, Some(Variant::Null), Some(Variant::from("norm"))]);
572
573        let variant_array = b.build();
574
575        assert_eq!(variant_array.len(), 3);
576        assert!(variant_array.is_null(0));
577        assert_eq!(variant_array.value(1), Variant::Null);
578        assert_eq!(
579            variant_array.value(2),
580            Variant::ShortString(ShortString::try_new("norm").unwrap())
581        );
582    }
583
584    #[test]
585    fn test_variant_value_array_builder_basic() {
586        let mut builder = VariantValueArrayBuilder::new(10);
587
588        // Append some values
589        builder.append_value(Variant::from(42i32));
590        builder.append_null();
591        builder.append_value(Variant::from("hello"));
592
593        let value_array = builder.build().unwrap();
594        assert_eq!(value_array.len(), 3);
595    }
596
597    #[test]
598    fn test_variant_value_array_builder_with_objects() {
599        // Populate a variant array with objects
600        let mut builder = VariantArrayBuilder::new(3);
601        builder
602            .new_object()
603            .with_field("name", "Alice")
604            .with_field("age", 30i32)
605            .finish();
606
607        builder
608            .new_object()
609            .with_field("name", "Bob")
610            .with_field("age", 42i32)
611            .with_field("city", "Wonderland")
612            .finish();
613
614        builder
615            .new_object()
616            .with_field("name", "Charlie")
617            .with_field("age", 1i32)
618            .finish();
619
620        let array = builder.build();
621
622        // Copy (some of) the objects over to the value array builder
623        //
624        // NOTE: Because we will reuse the metadata column, we cannot reorder rows. We can only
625        // filter or manipulate values within a row.
626        let mut value_builder = VariantValueArrayBuilder::new(3);
627
628        // straight copy
629        value_builder.append_value(array.value(0));
630
631        // filtering fields takes more work because we need to manually create an object builder
632        let value = array.value(1);
633        let mut builder = value_builder.builder_ext(value.metadata());
634        builder
635            .new_object()
636            .with_field("name", value.get_object_field("name").unwrap())
637            .with_field("age", value.get_object_field("age").unwrap())
638            .finish();
639
640        // same bytes, but now nested and duplicated inside a list
641        let value = array.value(2);
642        let mut builder = value_builder.builder_ext(value.metadata());
643        builder
644            .new_list()
645            .with_value(value.clone())
646            .with_value(value.clone())
647            .finish();
648
649        let array2 = VariantArray::from_parts(
650            array.metadata_column().clone(),
651            Arc::new(value_builder.build().unwrap()),
652            None,
653            None,
654        );
655
656        assert_eq!(array2.len(), 3);
657        assert_eq!(array.value(0), array2.value(0));
658
659        assert_eq!(
660            array.value(1).get_object_field("name"),
661            array2.value(1).get_object_field("name")
662        );
663        assert_eq!(
664            array.value(1).get_object_field("age"),
665            array2.value(1).get_object_field("age")
666        );
667
668        assert_eq!(array.value(2), array2.value(2).get_list_element(0).unwrap());
669        assert_eq!(array.value(2), array2.value(2).get_list_element(1).unwrap());
670    }
671}