This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new 9ca6eff70 feat(java): add schema typed row fields accessor (#3631)
9ca6eff70 is described below

commit 9ca6eff701f7b247cc41437d4baa7e2815740194
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Apr 28 21:33:29 2026 +0800

    feat(java): add schema typed row fields accessor (#3631)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    Closes #3616
    
    ## AI Contribution Checklist
    
    
    
    - [ ] Substantial AI assistance was used in this PR: `yes` / `no`
    - [ ] If `yes`, I included a completed [AI Contribution
    
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
    in this PR description and the required `AI Usage Disclosure`.
    - [ ] If `yes`, my PR description includes the required `ai_review`
    summary and screenshot evidence of the final clean AI review results
    from both fresh reviewers on the current PR diff or current HEAD after
    the latest code changes.
    
    
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 docs/guide/java/row-format.md                      |  13 +
 .../java/org/apache/fory/format/type/Schema.java   | 373 ++++++++++++++++++++-
 .../fory/format/type/SchemaRowFieldTest.java       | 232 +++++++++++++
 3 files changed, 617 insertions(+), 1 deletion(-)

diff --git a/docs/guide/java/row-format.md b/docs/guide/java/row-format.md
index 5b3425bc8..b99bf4527 100644
--- a/docs/guide/java/row-format.md
+++ b/docs/guide/java/row-format.md
@@ -71,6 +71,13 @@ BinaryArray f4Array = binaryRow.getArray(3);              // 
Access f4 list
 BinaryRow bar10 = f4Array.getStruct(10);                  // Access 11th Bar
 long value = bar10.getArray(1).getInt64(5);               // Access 6th 
element of bar.f2
 
+// Name-based access without repeated schema lookups
+Schema schema = encoder.schema();
+Schema.Int32Field f1 = schema.int32Field("f1");
+Schema.ArrayField f4 = schema.arrayField("f4");
+int f1Value = f1.get(binaryRow);
+ArrayData f4ByName = f4.get(binaryRow);
+
 // Partial deserialization - only deserialize what you need
 RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class);
 Bar bar1 = barEncoder.fromRow(f4Array.getStruct(10));     // Deserialize 11th 
Bar only
@@ -80,6 +87,12 @@ Bar bar2 = barEncoder.fromRow(f4Array.getStruct(20));     // 
Deserialize 21st Ba
 Foo newFoo = encoder.fromRow(binaryRow);
 ```
 
+Cache the returned `Schema.*Field` handles in user code and reuse them for all 
rows with the same
+schema. Calling `schema.int32Field("f1")` creates a typed handle by resolving 
the field name to an
+ordinal, accepting Java lower-camel field names for bean-derived schemas, 
validating the expected
+row-format type, and storing the resolved ordinal. Later calls such as 
`f1.get(binaryRow)` go
+straight to the ordinal row getter without another schema map lookup or typed 
handle construction.
+
 ## Key Benefits
 
 | Feature                 | Description                                        
    |
diff --git 
a/java/fory-format/src/main/java/org/apache/fory/format/type/Schema.java 
b/java/fory-format/src/main/java/org/apache/fory/format/type/Schema.java
index f21d1b6e6..7ce1049e7 100644
--- a/java/fory-format/src/main/java/org/apache/fory/format/type/Schema.java
+++ b/java/fory-format/src/main/java/org/apache/fory/format/type/Schema.java
@@ -19,14 +19,28 @@
 
 package org.apache.fory.format.type;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import org.apache.fory.format.row.ArrayData;
+import org.apache.fory.format.row.Getters;
+import org.apache.fory.format.row.MapData;
+import org.apache.fory.format.row.Row;
+import org.apache.fory.format.row.Setters;
+import org.apache.fory.util.StringUtils;
 
-/** Schema: a collection of named fields defining a row structure. */
+/**
+ * Schema: a collection of named fields defining a row structure.
+ *
+ * <p>For repeated row access by name, create typed field handles once with 
methods such as {@link
+ * #int32Field(String)} and reuse those handles for every row. Building a 
handle performs the schema
+ * name lookup, Java lower-camel fallback, and type validation once; 
subsequent get/set calls use
+ * its resolved ordinal directly.
+ */
 public class Schema {
   private final List<Field> fields;
   private final Map<String, Integer> nameToIndex;
@@ -78,6 +92,363 @@ public class Schema {
     return idx != null ? idx : -1;
   }
 
+  /** Creates a typed field handle for a boolean field. */
+  public BoolField boolField(String name) {
+    return new BoolField(typedField(name, DataTypes.TYPE_BOOL, 
DataTypes.bool().name()));
+  }
+
+  /** Creates a typed field handle for an int8 field. */
+  public Int8Field int8Field(String name) {
+    return new Int8Field(typedField(name, DataTypes.TYPE_INT8, 
DataTypes.int8().name()));
+  }
+
+  /** Creates a typed field handle for an int16 field. */
+  public Int16Field int16Field(String name) {
+    return new Int16Field(typedField(name, DataTypes.TYPE_INT16, 
DataTypes.int16().name()));
+  }
+
+  /** Creates a typed field handle for an int32 field. */
+  public Int32Field int32Field(String name) {
+    return new Int32Field(typedField(name, DataTypes.TYPE_INT32, 
DataTypes.int32().name()));
+  }
+
+  /** Creates a typed field handle for an int64 field. */
+  public Int64Field int64Field(String name) {
+    return new Int64Field(typedField(name, DataTypes.TYPE_INT64, 
DataTypes.int64().name()));
+  }
+
+  /** Creates a typed field handle for a float32 field. */
+  public Float32Field float32Field(String name) {
+    return new Float32Field(typedField(name, DataTypes.TYPE_FLOAT32, 
DataTypes.float32().name()));
+  }
+
+  /** Creates a typed field handle for a float64 field. */
+  public Float64Field float64Field(String name) {
+    return new Float64Field(typedField(name, DataTypes.TYPE_FLOAT64, 
DataTypes.float64().name()));
+  }
+
+  /** Creates a typed field handle for a date field. */
+  public DateField dateField(String name) {
+    return new DateField(typedField(name, DataTypes.TYPE_LOCAL_DATE, 
DataTypes.date32().name()));
+  }
+
+  /** Creates a typed field handle for a timestamp field. */
+  public TimestampField timestampField(String name) {
+    return new TimestampField(
+        typedField(name, DataTypes.TYPE_TIMESTAMP, 
DataTypes.timestamp().name()));
+  }
+
+  /** Creates a typed field handle for a decimal field. */
+  public DecimalField decimalField(String name) {
+    return new DecimalField(typedField(name, DataTypes.TYPE_DECIMAL, 
DataTypes.decimal().name()));
+  }
+
+  /** Creates a typed field handle for a string field. */
+  public StringField stringField(String name) {
+    return new StringField(typedField(name, DataTypes.TYPE_STRING, 
DataTypes.utf8().name()));
+  }
+
+  /** Creates a typed field handle for a binary field. */
+  public BinaryField binaryField(String name) {
+    return new BinaryField(typedField(name, DataTypes.TYPE_BINARY, 
DataTypes.binary().name()));
+  }
+
+  /** Creates a typed field handle for a struct field. */
+  public StructField structField(String name) {
+    return new StructField(typedField(name, DataTypes.TYPE_STRUCT, "struct"));
+  }
+
+  /** Creates a typed field handle for an array field. */
+  public ArrayField arrayField(String name) {
+    return new ArrayField(typedField(name, DataTypes.TYPE_LIST, "list"));
+  }
+
+  /** Creates a typed field handle for a map field. */
+  public MapField mapField(String name) {
+    return new MapField(typedField(name, DataTypes.TYPE_MAP, "map"));
+  }
+
+  private ResolvedField typedField(String name, int typeId, String 
expectedType) {
+    Integer ordinal = nameToIndex.get(name);
+    if (ordinal == null) {
+      ordinal = nameToIndex.get(StringUtils.lowerCamelToLowerUnderscore(name));
+    }
+    if (ordinal == null) {
+      throw new IllegalArgumentException("Field " + name + " doesn't exist in 
schema");
+    }
+    Field field = fields.get(ordinal);
+    if (field.type().typeId() != typeId) {
+      throw new IllegalArgumentException(
+          "Field " + name + " is " + field.type() + ", expected " + 
expectedType);
+    }
+    return new ResolvedField(ordinal, field);
+  }
+
+  private static final class ResolvedField {
+    private final int ordinal;
+    private final Field field;
+
+    private ResolvedField(int ordinal, Field field) {
+      this.ordinal = ordinal;
+      this.field = field;
+    }
+  }
+
+  /**
+   * Typed row-field handle.
+   *
+   * <p>Callers should create and store a handle once from {@link Schema}, 
then reuse it across rows
+   * of the same schema to avoid repeated name-to-index map lookups and 
repeated typed handle
+   * construction. Accessors keep only the resolved ordinal and schema {@link 
Field}; {@code
+   * get}/{@code set} delegate to ordinal row APIs.
+   */
+  public abstract static class RowField {
+    private final int ordinal;
+    private final Field field;
+
+    private RowField(ResolvedField field) {
+      this.ordinal = field.ordinal;
+      this.field = field.field;
+    }
+
+    public final String name() {
+      return field.name();
+    }
+
+    public final int ordinal() {
+      return ordinal;
+    }
+
+    public final Field field() {
+      return field;
+    }
+
+    public final boolean nullable() {
+      return field.nullable();
+    }
+
+    public final boolean isNullAt(Getters row) {
+      return row.isNullAt(ordinal);
+    }
+  }
+
+  public static final class BoolField extends RowField {
+    private BoolField(ResolvedField field) {
+      super(field);
+    }
+
+    public boolean get(Getters row) {
+      return row.getBoolean(ordinal());
+    }
+
+    public void set(Setters row, boolean value) {
+      row.setBoolean(ordinal(), value);
+    }
+  }
+
+  public static final class Int8Field extends RowField {
+    private Int8Field(ResolvedField field) {
+      super(field);
+    }
+
+    public byte get(Getters row) {
+      return row.getByte(ordinal());
+    }
+
+    public void set(Setters row, byte value) {
+      row.setByte(ordinal(), value);
+    }
+  }
+
+  public static final class Int16Field extends RowField {
+    private Int16Field(ResolvedField field) {
+      super(field);
+    }
+
+    public short get(Getters row) {
+      return row.getInt16(ordinal());
+    }
+
+    public void set(Setters row, short value) {
+      row.setInt16(ordinal(), value);
+    }
+  }
+
+  public static final class Int32Field extends RowField {
+    private Int32Field(ResolvedField field) {
+      super(field);
+    }
+
+    public int get(Getters row) {
+      return row.getInt32(ordinal());
+    }
+
+    public void set(Setters row, int value) {
+      row.setInt32(ordinal(), value);
+    }
+  }
+
+  public static final class Int64Field extends RowField {
+    private Int64Field(ResolvedField field) {
+      super(field);
+    }
+
+    public long get(Getters row) {
+      return row.getInt64(ordinal());
+    }
+
+    public void set(Setters row, long value) {
+      row.setInt64(ordinal(), value);
+    }
+  }
+
+  public static final class Float32Field extends RowField {
+    private Float32Field(ResolvedField field) {
+      super(field);
+    }
+
+    public float get(Getters row) {
+      return row.getFloat32(ordinal());
+    }
+
+    public void set(Setters row, float value) {
+      row.setFloat32(ordinal(), value);
+    }
+  }
+
+  public static final class Float64Field extends RowField {
+    private Float64Field(ResolvedField field) {
+      super(field);
+    }
+
+    public double get(Getters row) {
+      return row.getFloat64(ordinal());
+    }
+
+    public void set(Setters row, double value) {
+      row.setFloat64(ordinal(), value);
+    }
+  }
+
+  public static final class DateField extends RowField {
+    private DateField(ResolvedField field) {
+      super(field);
+    }
+
+    public int get(Getters row) {
+      return row.getDate(ordinal());
+    }
+
+    public void set(Setters row, int value) {
+      row.setDate(ordinal(), value);
+    }
+  }
+
+  public static final class TimestampField extends RowField {
+    private TimestampField(ResolvedField field) {
+      super(field);
+    }
+
+    public long get(Getters row) {
+      return row.getTimestamp(ordinal());
+    }
+
+    public void set(Setters row, long value) {
+      row.setTimestamp(ordinal(), value);
+    }
+  }
+
+  public static final class DecimalField extends RowField {
+    private DecimalField(ResolvedField field) {
+      super(field);
+    }
+
+    public BigDecimal get(Getters row) {
+      return row.getDecimal(ordinal());
+    }
+
+    public void set(Setters row, BigDecimal value) {
+      row.setDecimal(ordinal(), value);
+    }
+  }
+
+  public static final class StringField extends RowField {
+    private StringField(ResolvedField field) {
+      super(field);
+    }
+
+    public String get(Getters row) {
+      return row.getString(ordinal());
+    }
+
+    public void set(Setters row, String value) {
+      row.setString(ordinal(), value);
+    }
+  }
+
+  public static final class BinaryField extends RowField {
+    private BinaryField(ResolvedField field) {
+      super(field);
+    }
+
+    public byte[] get(Getters row) {
+      return row.getBinary(ordinal());
+    }
+
+    public void set(Setters row, byte[] value) {
+      row.setBinary(ordinal(), value);
+    }
+  }
+
+  public static final class StructField extends RowField {
+    private final Schema schema;
+
+    private StructField(ResolvedField field) {
+      super(field);
+      this.schema = DataTypes.schemaFromStructField(field.field);
+    }
+
+    public Row get(Getters row) {
+      return row.getStruct(ordinal());
+    }
+
+    public void set(Setters row, Row value) {
+      row.setStruct(ordinal(), value);
+    }
+
+    public Schema schema() {
+      return schema;
+    }
+  }
+
+  public static final class ArrayField extends RowField {
+    private ArrayField(ResolvedField field) {
+      super(field);
+    }
+
+    public ArrayData get(Getters row) {
+      return row.getArray(ordinal());
+    }
+
+    public void set(Setters row, ArrayData value) {
+      row.setArray(ordinal(), value);
+    }
+  }
+
+  public static final class MapField extends RowField {
+    private MapField(ResolvedField field) {
+      super(field);
+    }
+
+    public MapData get(Getters row) {
+      return row.getMap(ordinal());
+    }
+
+    public void set(Setters row, MapData value) {
+      row.setMap(ordinal(), value);
+    }
+  }
+
   public Map<String, String> metadata() {
     return Collections.unmodifiableMap(metadata);
   }
diff --git 
a/java/fory-format/src/test/java/org/apache/fory/format/type/SchemaRowFieldTest.java
 
b/java/fory-format/src/test/java/org/apache/fory/format/type/SchemaRowFieldTest.java
new file mode 100644
index 000000000..2e78f65d3
--- /dev/null
+++ 
b/java/fory-format/src/test/java/org/apache/fory/format/type/SchemaRowFieldTest.java
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.fory.format.type;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.fory.format.encoder.Encoders;
+import org.apache.fory.format.encoder.RowEncoder;
+import org.apache.fory.format.row.ArrayData;
+import org.apache.fory.format.row.MapData;
+import org.apache.fory.format.row.Row;
+import org.apache.fory.format.row.binary.BinaryRow;
+import org.apache.fory.format.row.binary.writer.BinaryRowWriter;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class SchemaRowFieldTest {
+
+  public static class Issue3616RowData {
+    public boolean fBool;
+    public byte fTiny;
+    public short fSmall;
+    public int fInt1;
+    public long fBigint;
+    public float fFloat;
+    public double fDouble;
+    public String fString;
+    public byte[] fBinary;
+    public int fDate;
+    public Long fTimestamp;
+    public long fDecimalRaw;
+    public int fDecimalScale;
+    public int fInt2;
+  }
+
+  public static class NestedBean {
+    public int nestedInt;
+    public String nestedString;
+  }
+
+  public static class CompositeBean {
+    public List<String> arrayValue;
+    public Map<String, Integer> mapValue;
+    public NestedBean nestedValue;
+  }
+
+  @Test
+  public void testNamedFieldHandlesUseSchemaOrder() {
+    RowEncoder<Issue3616RowData> encoder = 
Encoders.bean(Issue3616RowData.class);
+    Issue3616RowData data = new Issue3616RowData();
+    data.fBool = false;
+    data.fTiny = 1;
+    data.fSmall = 2;
+    data.fInt1 = 3;
+    data.fBigint = 4L;
+    data.fFloat = 5.0f;
+    data.fDouble = 6.0;
+    data.fString = "asdasd";
+    data.fBinary = new byte[] {1, 2, 3};
+    data.fDate = 7;
+    data.fTimestamp = 8L;
+    data.fDecimalRaw = 9L;
+    data.fDecimalScale = 10;
+    data.fInt2 = 15;
+
+    BinaryRow row = encoder.toRow(data);
+    Schema schema = encoder.schema();
+    Schema.Int32Field fInt2 = schema.int32Field("fInt2");
+
+    Assert.assertEquals(fInt2.name(), "f_int2");
+    Assert.assertEquals(fInt2.ordinal(), schema.getFieldIndex(fInt2.name()));
+    Assert.assertNotEquals(fInt2.ordinal(), 13);
+    Assert.assertEquals(fInt2.get(row), 15);
+    Assert.assertEquals(schema.stringField("fString").get(row), "asdasd");
+    Assert.assertFalse(schema.boolField("fBool").get(row));
+  }
+
+  @Test
+  public void testTypedScalarFieldReads() {
+    Schema schema =
+        DataTypes.schema(
+            DataTypes.field("boolValue", DataTypes.bool(), false),
+            DataTypes.field("byteValue", DataTypes.int8(), false),
+            DataTypes.field("shortValue", DataTypes.int16(), false),
+            DataTypes.field("intValue", DataTypes.int32(), false),
+            DataTypes.field("longValue", DataTypes.int64(), false),
+            DataTypes.field("floatValue", DataTypes.float32(), false),
+            DataTypes.field("doubleValue", DataTypes.float64(), false),
+            DataTypes.field("dateValue", DataTypes.date32(), false),
+            DataTypes.field("timestampValue", DataTypes.timestamp(), false),
+            DataTypes.field("decimalValue", DataTypes.decimal(10, 2)),
+            DataTypes.field("stringValue", DataTypes.utf8()),
+            DataTypes.field("binaryValue", DataTypes.binary()));
+
+    BinaryRowWriter writer = new BinaryRowWriter(schema);
+    writer.reset();
+    writer.write(0, true);
+    writer.write(1, (byte) 2);
+    writer.write(2, (short) 3);
+    writer.write(3, 4);
+    writer.write(4, 5L);
+    writer.write(5, 6.0f);
+    writer.write(6, 7.0d);
+    writer.write(7, 8);
+    writer.write(8, 9L);
+    writer.write(9, BigDecimal.valueOf(12345, 2));
+    writer.write(10, "fory");
+    writer.write(11, new byte[] {10, 11, 12});
+    BinaryRow row = writer.getRow();
+
+    Assert.assertTrue(schema.boolField("boolValue").get(row));
+    Assert.assertEquals(schema.int8Field("byteValue").get(row), (byte) 2);
+    Assert.assertEquals(schema.int16Field("shortValue").get(row), (short) 3);
+    Assert.assertEquals(schema.int32Field("intValue").get(row), 4);
+    Assert.assertEquals(schema.int64Field("longValue").get(row), 5L);
+    Assert.assertEquals(schema.float32Field("floatValue").get(row), 6.0f);
+    Assert.assertEquals(schema.float64Field("doubleValue").get(row), 7.0d);
+    Assert.assertEquals(schema.dateField("dateValue").get(row), 8);
+    Assert.assertEquals(schema.timestampField("timestampValue").get(row), 9L);
+    Assert.assertEquals(schema.decimalField("decimalValue").get(row), 
BigDecimal.valueOf(12345, 2));
+    Assert.assertEquals(schema.stringField("stringValue").get(row), "fory");
+    Assert.assertEquals(schema.binaryField("binaryValue").get(row), new byte[] 
{10, 11, 12});
+  }
+
+  @Test
+  public void testTypedFixedWidthFieldSetters() {
+    Schema schema =
+        DataTypes.schema(
+            DataTypes.field("boolValue", DataTypes.bool(), false),
+            DataTypes.field("byteValue", DataTypes.int8(), false),
+            DataTypes.field("shortValue", DataTypes.int16(), false),
+            DataTypes.field("intValue", DataTypes.int32(), false),
+            DataTypes.field("longValue", DataTypes.int64(), false),
+            DataTypes.field("floatValue", DataTypes.float32(), false),
+            DataTypes.field("doubleValue", DataTypes.float64(), false),
+            DataTypes.field("dateValue", DataTypes.date32(), false),
+            DataTypes.field("timestampValue", DataTypes.timestamp(), false));
+
+    BinaryRowWriter writer = new BinaryRowWriter(schema);
+    writer.reset();
+    for (int i = 0; i < schema.numFields(); i++) {
+      writer.write(i, 0L);
+    }
+    BinaryRow row = writer.getRow();
+
+    schema.boolField("boolValue").set(row, true);
+    schema.int8Field("byteValue").set(row, (byte) 1);
+    schema.int16Field("shortValue").set(row, (short) 2);
+    schema.int32Field("intValue").set(row, 3);
+    schema.int64Field("longValue").set(row, 4L);
+    schema.float32Field("floatValue").set(row, 5.0f);
+    schema.float64Field("doubleValue").set(row, 6.0d);
+    schema.dateField("dateValue").set(row, 7);
+    schema.timestampField("timestampValue").set(row, 8L);
+
+    Assert.assertTrue(schema.boolField("boolValue").get(row));
+    Assert.assertEquals(schema.int8Field("byteValue").get(row), (byte) 1);
+    Assert.assertEquals(schema.int16Field("shortValue").get(row), (short) 2);
+    Assert.assertEquals(schema.int32Field("intValue").get(row), 3);
+    Assert.assertEquals(schema.int64Field("longValue").get(row), 4L);
+    Assert.assertEquals(schema.float32Field("floatValue").get(row), 5.0f);
+    Assert.assertEquals(schema.float64Field("doubleValue").get(row), 6.0d);
+    Assert.assertEquals(schema.dateField("dateValue").get(row), 7);
+    Assert.assertEquals(schema.timestampField("timestampValue").get(row), 8L);
+  }
+
+  @Test
+  public void testTypedCompositeFieldReads() {
+    CompositeBean bean = new CompositeBean();
+    bean.arrayValue = Arrays.asList("a", "b");
+    bean.mapValue = new HashMap<>();
+    bean.mapValue.put("k", 10);
+    bean.nestedValue = new NestedBean();
+    bean.nestedValue.nestedInt = 7;
+    bean.nestedValue.nestedString = "nested";
+
+    RowEncoder<CompositeBean> encoder = Encoders.bean(CompositeBean.class);
+    BinaryRow row = encoder.toRow(bean);
+    Schema schema = encoder.schema();
+
+    ArrayData array = schema.arrayField("arrayValue").get(row);
+    Assert.assertEquals(array.numElements(), 2);
+    Assert.assertEquals(array.getString(0), "a");
+    Assert.assertEquals(array.getString(1), "b");
+
+    MapData map = schema.mapField("mapValue").get(row);
+    Assert.assertEquals(map.numElements(), 1);
+    Assert.assertEquals(map.keyArray().getString(0), "k");
+    Assert.assertEquals(map.valueArray().getInt32(0), 10);
+
+    Schema.StructField nestedField = schema.structField("nestedValue");
+    Row nestedRow = nestedField.get(row);
+    Schema nestedSchema = nestedField.schema();
+    Assert.assertEquals(nestedSchema.int32Field("nestedInt").get(nestedRow), 
7);
+    
Assert.assertEquals(nestedSchema.stringField("nestedString").get(nestedRow), 
"nested");
+  }
+
+  @Test
+  public void testTypedFieldValidation() {
+    Schema schema =
+        DataTypes.schema(
+            DataTypes.field("name", DataTypes.utf8()), 
DataTypes.field("score", DataTypes.int32()));
+
+    IllegalArgumentException missing =
+        Assert.expectThrows(IllegalArgumentException.class, () -> 
schema.int32Field("missing"));
+    Assert.assertEquals(missing.getMessage(), "Field missing doesn't exist in 
schema");
+
+    IllegalArgumentException wrongType =
+        Assert.expectThrows(IllegalArgumentException.class, () -> 
schema.int32Field("name"));
+    Assert.assertEquals(wrongType.getMessage(), "Field name is utf8, expected 
int32");
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to