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-site.git
commit 751c40a47d6a47eced47b3c0b8c7dab10e7a9dad Author: chaokunyang <[email protected]> AuthorDate: Fri Jun 5 06:01:28 2026 +0000 π synced local 'docs/guide/' with remote 'docs/guide/' --- docs/guide/cpp/schema-evolution.md | 12 +++++++++++- docs/guide/csharp/schema-evolution.md | 9 +++++++++ docs/guide/dart/schema-evolution.md | 16 +++++++++++++++- docs/guide/go/schema-evolution.md | 31 +++++++++++++++++++++++++++++-- docs/guide/java/schema-evolution.md | 9 +++++++++ docs/guide/javascript/schema-evolution.md | 9 +++++++++ docs/guide/kotlin/type-serialization.md | 9 +++++++++ docs/guide/python/schema-evolution.md | 15 +++++++++++++++ docs/guide/rust/schema-evolution.md | 13 ++++++++++++- docs/guide/scala/type-serialization.md | 9 +++++++++ docs/guide/swift/schema-evolution.md | 17 ++++++++++++++++- docs/guide/xlang/field-nullability.md | 6 ++++-- 12 files changed, 147 insertions(+), 8 deletions(-) diff --git a/docs/guide/cpp/schema-evolution.md b/docs/guide/cpp/schema-evolution.md index 9844a26dec..f266a0a250 100644 --- a/docs/guide/cpp/schema-evolution.md +++ b/docs/guide/cpp/schema-evolution.md @@ -98,9 +98,19 @@ Compatible mode supports the following schema changes: | Remove fields | Supported | Extra fields are skipped | | Reorder fields | Supported | Fields matched by name, not position | | Change nullability | Supported | `T` β `std::optional<T>` | -| Change field types | Not supported | Types must be compatible | +| Change field types | Selected | Scalar changes must be lossless | | Rename fields | Not supported | Field names must match (case-sensitive) | +Compatible readers can handle selected scalar field type changes when the value is lossless. A +matched field can read between `bool`, `std::string`, numeric scalars, and decimal fields when the +converted value has the same logical value. Boolean strings must be exactly `"0"`, `"1"`, `"true"`, +or `"false"`. Numeric strings must use finite ASCII decimal notation without whitespace, a leading +`+`, Unicode digits, separators, or non-finite values such as `NaN` and `Infinity`. Numbers and +decimals can be read as canonical strings, and numeric widening or narrowing succeeds only when no +precision or range is lost. Nullable and optional field wrappers still compose with these conversions, +but reference-tracked scalar type changes are incompatible. Invalid strings and lossy conversions fail +during deserialization. + ## Adding Fields (Backward Compatibility) When deserializing old data with a new schema that has additional fields: diff --git a/docs/guide/csharp/schema-evolution.md b/docs/guide/csharp/schema-evolution.md index 108634b773..c0a6566531 100644 --- a/docs/guide/csharp/schema-evolution.md +++ b/docs/guide/csharp/schema-evolution.md @@ -31,6 +31,15 @@ Fory fory = Fory.Builder() Compatible mode writes type metadata that allows readers and writers with different struct definitions to interoperate. +Compatible readers also tolerate selected scalar field type changes when the value is lossless. A +matched field can read between `bool`, `string`, numeric scalars, and `decimal` when the converted +value has the same logical value. Boolean strings must be exactly `"0"`, `"1"`, `"true"`, or +`"false"`. Numeric strings use finite ASCII decimal syntax without whitespace, a leading plus sign, +Unicode digits, underscores, hexadecimal notation, `NaN`, or infinities. Numbers and decimals read as +strings use canonical plain decimal text. Nullable fields still compose with these conversions, but +reference-tracked scalar type changes are incompatible. Invalid strings, out-of-range values, and lossy +numeric conversions fail during deserialization. + ## Example: Add a Field ```csharp diff --git a/docs/guide/dart/schema-evolution.md b/docs/guide/dart/schema-evolution.md index 51777b8372..2e8fe46556 100644 --- a/docs/guide/dart/schema-evolution.md +++ b/docs/guide/dart/schema-evolution.md @@ -34,6 +34,17 @@ final fory = Fory(); In compatible mode, Fory includes enough field metadata in each message so that the reader can skip unknown fields and use defaults for missing ones. Use stable field IDs (see below) to anchor the schema across changes. +Compatible readers also tolerate selected scalar field type changes when the value is lossless. A +matched field can read between `bool`, `String`, numeric scalars, and `Decimal` when the converted +value has the same logical value. For example, `"true"` and `"false"` can be read as booleans, +`"123"` can be read as a numeric field that can hold `123`, numbers and decimals can be read as +canonical strings, and numeric widening or narrowing succeeds only when no precision or range is +lost. Scalar conversion applies only to matched compatible fields, not root values or collection +elements. String-to-number conversion accepts finite ASCII decimal literals without whitespace, a +leading `+`, Unicode digits, underscores, `NaN`, or `Infinity`. Nullable fields still compose with +these conversions, but reference-tracked scalar type changes are incompatible. Invalid strings, +out-of-range values, and lossy conversions fail with `InvalidDataException` during deserialization. + ### Schema-Consistent Mode Both sides must have the same model. Fory validates that the schemas match and will reject messages from a different schema version. Use this when all services are always updated together and you want schema mismatches to be caught as fast errors. @@ -68,11 +79,14 @@ If you add field IDs after payloads are already in production, existing stored m - Add a new optional field with a new, unused field ID. - Rename a field β as long as the `@ForyField(id: ...)` stays the same. - Remove a field β the peer will just ignore the missing value and use the Dart default. +- Change selected scalar field types when all deployed values convert without precision or range + loss. **Unsafe changes** (may break existing messages): - Reuse an existing field ID for a different field. -- Change a field's type to an incompatible type (e.g., `@ForyField(type: Int32Type()) int` β `String`). +- Change a field's type to an incompatible type or to a scalar type that cannot represent the peer + values exactly. - Change the registration identity (`id` or `name`) of a type after messages are in production. - Change a field's logical meaning without changing its ID. diff --git a/docs/guide/go/schema-evolution.md b/docs/guide/go/schema-evolution.md index 2c5e04d2f0..509fd7c042 100644 --- a/docs/guide/go/schema-evolution.md +++ b/docs/guide/go/schema-evolution.md @@ -159,6 +159,33 @@ type PersonV2 struct { Compatible mode handles this automatically by matching fields by name. +### Compatible Scalar Field Changes + +Compatible mode can also read selected scalar type changes for matched top-level +struct fields when the serialized value converts without changing its logical +value: + +- `bool` fields can be read from strings that are exactly `"0"`, `"1"`, + `"true"`, or `"false"`. Bool values read as strings become `"true"` or + `"false"`, and numeric `0` and `1` can be read as bools. +- Integer, unsigned integer, floating point, and decimal fields can be read + across numeric scalar types only when the value is represented exactly by the + target field type. +- Numeric fields can be read from strings only when the string is a finite ASCII + decimal literal with no whitespace, leading `+`, Unicode digits, separators, + radix prefixes, or special values such as `NaN` and `Infinity`. +- Numeric fields read as strings use canonical output: integers have normal + decimal text, floating point values use exact plain decimal text with a + decimal point, and decimals omit insignificant trailing fractional zeros. + +Scalar conversion composes with pointer and `optional.Optional[T]` fields when +the matched top-level scalar field is not reference-tracked. If a remote +nullable or optional field is absent, the local field follows the normal +missing/null compatible-mode behavior. Reference-tracked scalar type changes are +incompatible. If a present value cannot be converted losslessly, +deserialization fails with a data error instead of treating the field as +missing. + ## Incompatible Changes Some changes are NOT supported, even in compatible mode: @@ -168,11 +195,11 @@ Some changes are NOT supported, even in compatible mode: ```go // NOT SUPPORTED type V1 struct { - Value int32 // int32 + Value []int32 // list of int32 } type V2 struct { - Value string // Changed to string - INCOMPATIBLE + Value []string // Element type changed - INCOMPATIBLE } ``` diff --git a/docs/guide/java/schema-evolution.md b/docs/guide/java/schema-evolution.md index 21270cc1a3..5666e2f4a1 100644 --- a/docs/guide/java/schema-evolution.md +++ b/docs/guide/java/schema-evolution.md @@ -40,6 +40,15 @@ option unless they previously overrode the schema mode. In this compatible mode, deserialization can handle schema changes such as missing or extra fields, allowing it to succeed even when the serialization and deserialization processes have different class schemas. +Compatible readers also tolerate selected scalar field type changes when the value is lossless. A +matched field can read between `boolean`, `String`, numeric scalars, and `BigDecimal` when the value +has the same logical value after conversion. For example, `"true"` and `"false"` can be read as +booleans, `"123"` can be read as a numeric field that can hold `123`, numbers and decimals can be +read as canonical strings, and numeric widening or narrowing succeeds only when no precision or range +is lost. Numeric strings use finite ASCII decimal syntax. Nullable and boxed fields still compose with +these conversions, but reference-tracked scalar type changes are incompatible. Invalid strings and +lossy conversions fail during deserialization. + ```java Fory fory = Fory.builder().withXlang(false) .withCompatible(true) diff --git a/docs/guide/javascript/schema-evolution.md b/docs/guide/javascript/schema-evolution.md index 4f9e0eac6a..95d39f32cd 100644 --- a/docs/guide/javascript/schema-evolution.md +++ b/docs/guide/javascript/schema-evolution.md @@ -26,6 +26,15 @@ Schema evolution lets different versions of your service exchange messages safel - **Compatible mode** (default): writes extra field metadata so readers can skip unknown fields and tolerate missing ones. Good for independent deployments, rolling upgrades, and xlang services. - **Schema-consistent mode**: more compact, but both sides must have exactly the same schema. Use it only when schemas do not change, or when all services update together. +Compatible readers also tolerate selected scalar field type changes when the conversion is lossless. +A matched field can read between `boolean`, `string`, numeric scalars, and `Decimal` when the +converted value has the same logical value. For example, `"true"`, `"false"`, `"1"`, and `"0"` can +be read as booleans, exact finite ASCII numeric strings can be read as numeric fields that can hold +them, numbers and decimals can be read as canonical strings, and numeric widening or narrowing +succeeds only when no precision or range is lost. Invalid strings and lossy conversions fail during +deserialization. Nullable fields still compose with these conversions, but reference-tracked scalar +type changes are incompatible. + ## Default Compatible Mode ```ts diff --git a/docs/guide/kotlin/type-serialization.md b/docs/guide/kotlin/type-serialization.md index 1acb10db94..6bc3bbcb23 100644 --- a/docs/guide/kotlin/type-serialization.md +++ b/docs/guide/kotlin/type-serialization.md @@ -23,6 +23,15 @@ This page covers serialization of Kotlin-specific JVM types in native mode. For cross-language Kotlin models, use the xlang path described in [Static Generated Serializers](static-generated-serializers.md). +When compatible mode is enabled, Kotlin readers use the JVM compatible-read rules for selected +scalar field type changes. A matched field can read between `Boolean`, `String`, numeric scalars, +and `java.math.BigDecimal` when the converted value has the same logical value. For example, +`"true"` and `"false"` can be read as booleans, `"123"` can be read as a numeric field that can hold +`123`, numbers and decimals can be read as canonical strings, and numeric widening or narrowing +succeeds only when no precision or range is lost. Numeric strings use finite ASCII decimal syntax. +Invalid strings and lossy conversions fail during deserialization. Nullable and boxed fields still +compose with these conversions, but reference-tracked scalar type changes are incompatible. + ## Setup All examples assume the following setup: diff --git a/docs/guide/python/schema-evolution.md b/docs/guide/python/schema-evolution.md index 182d54c90f..402978bb7e 100644 --- a/docs/guide/python/schema-evolution.md +++ b/docs/guide/python/schema-evolution.md @@ -23,6 +23,21 @@ Apache Foryβ’ supports schema evolution in compatible mode, allowing fields to while maintaining compatibility. Xlang mode enables compatible mode by default. In native mode, set `compatible=True` explicitly when Python-only payloads need schema evolution. +Compatible readers also tolerate selected scalar field type changes when the value is lossless. A +matched field can read between `bool`, `str`, numeric scalars, and `Decimal` when the converted value +has the same logical value. For example, `"true"`, `"false"`, `"0"`, and `"1"` can be read as +booleans; `"123"` can be read as a numeric field that can hold `123`; numbers and decimals can be +read as canonical strings; and numeric widening or narrowing succeeds only when no precision or range +is lost. + +Scalar conversion is only applied to matched compatible fields, not to root values or collection +elements. String-to-number conversion accepts finite ASCII decimal literals without whitespace, a +leading `+`, Unicode digits, underscores, or special values such as `NaN` and `Infinity`. Invalid +strings, out-of-range values, and lossy conversions fail with `pyfory.error.ForyInvalidDataError` +during deserialization. +Optional and nullable fields still compose with these conversions, but reference-tracked scalar type +changes are incompatible. + ## Xlang Default ```python diff --git a/docs/guide/rust/schema-evolution.md b/docs/guide/rust/schema-evolution.md index 14c536afc4..f3e3bb3d7c 100644 --- a/docs/guide/rust/schema-evolution.md +++ b/docs/guide/rust/schema-evolution.md @@ -89,12 +89,23 @@ struct StableMessage { - Remove obsolete fields (skipped during deserialization) - Change field nullability (`T` β `Option<T>`) - Reorder fields (matched by name, not position) +- Change selected scalar field types when the value converts without precision or range loss - Type-safe fallback to default values for missing fields +Compatible readers can handle selected scalar field type changes when the value is lossless. A +matched field can read between `bool`, `String`, numeric scalars, and decimal fields when the +converted value has the same logical value. For example, `"true"` and `"false"` can be read as +booleans, `"123"` can be read as a numeric field that can hold `123`, numbers and decimals can be +read as canonical strings, and numeric widening or narrowing succeeds only when no precision or range +is lost. Numeric strings use finite ASCII decimal syntax. Optional fields still compose with these +conversions, but reference-tracked scalar type changes are incompatible. Invalid strings and lossy +conversions fail during deserialization. + ## Compatibility Rules - Field names must match (case-sensitive) -- Type changes are not supported (except nullable/non-nullable) +- Type changes are supported only for nullable/non-nullable changes and selected lossless scalar + conversions - Nested struct types must be registered on both sides ## Enum Support diff --git a/docs/guide/scala/type-serialization.md b/docs/guide/scala/type-serialization.md index 00a8574a84..44856c584a 100644 --- a/docs/guide/scala/type-serialization.md +++ b/docs/guide/scala/type-serialization.md @@ -23,6 +23,15 @@ This page covers serialization of Scala-specific JVM types in native mode. For cross-language Scala models, use the xlang path described in [Schema IDL And Xlang](schema-idl.md). +When compatible mode is enabled, Scala readers use the JVM compatible-read rules for selected +scalar field type changes. A matched field can read between `Boolean`, `String`, numeric scalars, +and `java.math.BigDecimal` when the converted value has the same logical value. For example, +`"true"` and `"false"` can be read as booleans, `"123"` can be read as a numeric field that can hold +`123`, numbers and decimals can be read as canonical strings, and numeric widening or narrowing +succeeds only when no precision or range is lost. Numeric strings use finite ASCII decimal syntax. +Invalid strings and lossy conversions fail during deserialization. Optional and boxed fields still +compose with these conversions, but reference-tracked scalar type changes are incompatible. + ## Setup All examples assume the following setup: diff --git a/docs/guide/swift/schema-evolution.md b/docs/guide/swift/schema-evolution.md index f7fe019136..a585076674 100644 --- a/docs/guide/swift/schema-evolution.md +++ b/docs/guide/swift/schema-evolution.md @@ -21,6 +21,18 @@ license: | Fory supports schema evolution through compatible mode, which is enabled by default in Swift. +Compatible readers also tolerate selected scalar field type changes when the value is lossless. A +matched field can read between `Bool`, `String`, numeric scalars, and `Decimal` when the converted +value has the same logical value. For example, `"true"` and `"false"` can be read as booleans, +`"123"` can be read as a numeric field that can hold `123`, numbers and decimals can be read as +canonical strings, and numeric widening or narrowing succeeds only when no precision or range is +lost. Numeric strings use finite ASCII decimal syntax. Invalid strings and lossy conversions fail +during deserialization. + +Scalar conversion also composes with optional fields. A present optional value is converted by the +same rules, while a missing optional value keeps Swift's normal compatible-mode default for the +local field. Reference-tracked scalar type changes are incompatible. + ## Default Compatible Mode ```swift @@ -66,10 +78,13 @@ assert(v2.phone == nil) - Add new fields - Remove old fields - Reorder fields +- Change a matched scalar field between `Bool`, `String`, numeric scalars, or `Decimal` when every + value you write is lossless for the reader ## What Is Not Safe -- Arbitrary type changes for an existing field (for example `Int32` to `String`) +- Arbitrary type changes for an existing field, including scalar values that are out of range, + rounded, non-finite, or not accepted by the compatible numeric string grammar - Inconsistent registration mapping across peers ## Schema-consistent Mode Behavior diff --git a/docs/guide/xlang/field-nullability.md b/docs/guide/xlang/field-nullability.md index b10c06dc3e..7113c629a3 100644 --- a/docs/guide/xlang/field-nullability.md +++ b/docs/guide/xlang/field-nullability.md @@ -242,14 +242,16 @@ When a non-nullable field receives a null value: ## Schema Compatibility -The nullable flag is part of the struct schema fingerprint. Changing a field's nullability is a **breaking change** that will cause schema version mismatch errors. +The nullable flag is part of the struct schema fingerprint. In schema-consistent reads, changing a field's nullability is a **breaking change** that will cause schema version mismatch errors. ``` Schema A: { name: String (non-nullable) } Schema B: { name: String (nullable) } -// These have different fingerprints and are incompatible +// These have different fingerprints for schema-consistent reads ``` +In compatible mode, top-level scalar fields can still be matched when their scalar type is otherwise compatible and the nullability or optional wrapper differs. Present values are read through compatible scalar conversion and must satisfy the normal lossless conversion checks. Remote null values follow the compatible-read null/default behavior for the local field. + ## Best Practices 1. **Use non-nullable by default**: Only make fields nullable when null is a valid semantic value --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
