The checkout billing and delivery address forms accept any text for each field as long as the mandatory fields are present. There is no way to validate that a postal code, phone number, VAT ID, or other structured field actually matches an expected format. This leads to bad address data being saved and passed downstream to shipping and invoicing systems.

We need configurable per‑field format validation that runs during address submission, on both the billing and delivery address steps. The behavior should work like the existing mandatory‑field checks: if validation fails, an error message should be shown for the invalid field(s), the checkout should not advance, and the field should be reported in the same error container that reports missing fields.

Requirements:
- Allow an admin/deployer to define a map of field identifiers to Perl‑compatible regular expressions (PCRE). The field identifiers should be the same as those already used for mandatory/optional/hidden configuration (e.g. `order.base.address.postal`, `order.base.address.telephone`, etc.).
- Validations should apply only to fields that are explicitly configured; if no validation is defined for a field, it should be accepted as before.
- Fields that are unknown (not defined in the mandatory/optional set) should still be silently removed before validation.
- If a field’s value does not match its configured regular expression, the submission must be rejected with an error, just as if that mandatory field were missing. The error should identify the field (e.g. “Billing address part ‘postal’ is invalid” / “Delivery address part ‘postal’ is invalid”).
- Format validation should happen after stripping unknown fields and handling the company/vatID fallback, but before the mandatory‑missing check, so that an invalid value is reported even if the field is also mandatory. A single error array should collect both “missing” and “invalid” problems.

The goal is to be able to write configuration like:
`ca‑billing.postal = '/^[0-9]{5}$/'` (or the equivalent hierarchical key via the existing Arcavias configuration system) and have that pattern enforced on the checkout form.

This is a missing capability in the client‑html checkout address components (Billing and Delivery defaults). The existing `_checkFields` logic only looks for missing mandatory fields; we need it to also validate format when an optional regex map is supplied.

Implements configurable validation of address field data


We'd like to change the filter pipeline in the manager base class so
that filter closures can transform items, not just vote accept/reject.

Acceptance criteria:
- When a filter callback is registered via `addFilter()`, the callback
  should now return the item (possibly after modification) to keep it,
  or `null` to discard it.
- The `filter()` method must therefore return the item object itself
  (the final item after all applicable callbacks have run) or `null` if
  any callback returned `null`.
- Since callbacks are called in order, if one returns a modified item,
  subsequent callbacks should receive that modified item.
- The `addFilter()` method no longer needs to return the manager
  instance; its fluent interface can be removed.

The previous contract (closure returning `true`/`false` and `filter()`
returning `bool`) must be replaced completely by the item-or-null
contract described above.  Existing code that relied on the old boolean
behavior should break, but this is the intended direction.

Return item or null for manager filter() method


**Bug Report: Order service items missing taxRates support; XML export fails with non-scalar price values**

**Observed problem**
1. When I load an order and access its service items, the tax rate details (e.g., multiple tax rates) are not available. Unlike order product items, the service items do not expose `taxrates` through their `toArray()` / `fromArray()` methods. This means any code that relies on serializing/deserializing order service items (such as exporting orders, or integrating with external systems) cannot retrieve or restore the detailed tax rate breakdown.

2. The delivery service provider XML template (`lib/mshoplib/templates/service/provider/delivery/xml-item-standard.php`) directly outputs every value returned by `toArray()` into CDATA sections without checking whether the value is a scalar. Because product items already return an array for `taxrates`, and service items now need to do the same, this template breaks with a PHP error when it encounters an array value.

**Expected behavior**
- Order service items should support the same price field set as order product items, specifically including `taxrates` (i.e., `getTaxRates()` / `setTaxRates()`). When calling `toArray(true)`, the result should contain `order.base.service.taxrates` with the full tax rates array. `fromArray()` should accept `order.base.service.taxrates` as an array and set the tax rates on the price object.
- The delivery XML template should be robust against non-scalar values (like arrays) returned by `toArray()`. It should convert arrays to a JSON string before inserting them into CDATA sections.
- Cloning a service item should also clone its price object to avoid shared mutable state (mirroring how product items already behave).

**Context**
This inconsistency between order product and service item handling causes silent data loss when systems rely on the `toArray()`/`fromArray()` contract for price fields. The immediate trigger is the addition of per-tax-rate details in order product items, which works there but is absent for services, and the XML export is the most visible failure point.

Same price handling in order product and service items


supervision_value: 2
difficulty: 2
reasoning: The fix is a localized dead-code removal across a small number of files. The core behavior change is straightforward: drop a config key and parameter that are no longer used by the password helper. The agent needs to trace the salt from config through the manager into the item and up to the encode call, then remove it. This teaches some useful, narrow investigation (following constructor dependencies and verifying helper semantics), but the path is direct and the edit non-ambiguous.

---

The customer password encoding uses a configurable salt defined by `mshop/customer/manager/salt`. This salt is read from configuration in the customer manager and passed to the customer item, which stores it and passes it to the password helper’s `encode` method. The password helper now handles salting internally, making the external salt redundant and unused.

Remove the unused salt configuration and all associated dead code:
- Delete the `mshop/customer/manager/salt` configuration key and its documentation.
- Remove the salt property from the customer manager and customer item.
- Drop the salt parameter from the customer item constructor.
- Stop passing the salt when calling the password helper’s `encode` method.

Password encoding behavior must remain unchanged: the configured password helper should continue to encode passwords using its own internal salting mechanism.


**Implementation request: Catalog items incorrectly marked as modified after loading from database**

**Summary**
After retrieving a catalog item via search or fetch, calling `isModified()` on the item returns `true` even though no programmatic changes have been applied. This leads to unexpected save operations or other side effects downstream.

**Current behavior (symptom)**
`MShop_Catalog_Item_Standard` instances that are loaded from the database always appear modified. The issue is present immediately after construction by `MShop_Catalog_Manager_Base`.

**Expected behavior**
- `isModified()` must return `false` for a freshly loaded catalog item.
- The item should still correctly report modifications when properties are changed explicitly after construction (e.g., through `setConfig()`).
- Accessor/mutator methods for other properties—`getSiteId()`, `getUrl()`, `getTarget()`, `getTimeModified()`, `getTimeCreated()`, `getEditor()`, `getLevel()`, `getParentId()`—should not cause the item to become modified.
- The tree node’s own `isModified()` state (affected by structural changes like adding/removing children) must still contribute to the item’s `isModified()` result, but the initial population of node properties during retrieval should not trigger the node’s modified flag.

**Technical context**
The catalog item wraps a `\Aimeos\MW\Tree\Node\Iface` (for tree structure) and extends `\Aimeos\MShop\Common\Item\Base` (for general item storage). Currently, database values are assigned directly to the tree node when building the item, which causes the node to be marked modified. The fix should ensure that data properties that are not structural tree attributes (such as config) are stored in the base item’s data storage, while structural properties are set on the node without altering its modified status during item construction.

**Acceptance tests**
- A catalog item built from a database search result must have `isModified()` equal to `false`.
- After calling `setConfig()` with new values, `isModified()` must return `true`.
- All existing catalog item tests should continue to pass.

Avoid setting catalog node items to modified state


**Title:** Customer address & group should not be scoped to a site

**Description**
At present the customer address and group entities carry a mandatory `siteid` column and expose a corresponding `siteid` search attribute in their managers. This ties them to a particular site, but the integration packages treat addresses and groups as site‑independent resources. We need the core to behave the same way.

**Desired behaviour**

1. **Site‑agnostic data** – The `mshop_customer_address` and `mshop_customer_group` tables should allow `NULL` in their `siteid` column (currently they forbid `NULL`).
2. **No site‑based filtering** – The address manager, group manager, and the main customer manager must no longer offer `siteid` as a searchable field. Searching or filtering by site should not be possible through the public search configuration of these managers.
3. **Clean‑up completeness** – When site records are cleaned up, the customer manager should also delegate the cleanup to the group sub‑manager. (Address and list sub‑managers are already cleaned; groups are missing.)

**Scope & constraints**

- Do **not** alter the main `mshop_customer` table’s schema (it already allows `NULL` for `siteid`).
- Do **not** change how the managers internally store or read the `siteid` field; only the public search configuration and the schema nullability are in scope.
- Existing unit tests that assert `siteid` is non‑null or filterable for customer/address/group must be updated to match the new “no site scoping” reality.
- This change must be backward‑compatible for any code that does not rely on the removed search attributes; no other behavioural side‑effects.

Don't use siteid for customer address and group


**Bug:** `List` setter fields lose generic item type and format information when the owning class has a `@JSONField`-annotated constructor parameter

**Description**
When a JavaBean uses a custom constructor with a `@JSONField`-annotated parameter (e.g., `public Bean(@JSONField(name="id") int id)`), and also has a setter method for a generic list (e.g., `setValues(List<Long> values)`), the item type of the list field is not correctly resolved. Calling `FieldReader#getItemType()` on the list reader returns `Object.class` instead of `Long.class`. Consequently, element types are treated as raw `Object`, which can break downstream processing that expects the concrete type (e.g., `Long`).

The issue also affects proper propagation of field-level configuration such as `format` and `JSONSchema` to the list reader, because the current `FieldReaderListMethod` constructor overload that accepts an explicitly resolved `itemType` does not pass these attributes to the parent class, causing them to be silently lost.

**Steps to reproduce**
1. Define a class with a constructor that has a `@JSONField`-annotated parameter and a setter for a parameterized `List` type:
   ```java
   class Bean {
       private int id;
       private List<Long> values;
       public Bean(@JSONField(name = "id") int id) { this.id = id; }
       public void setValues(List<Long> values) { this.values = values; }
       public List<Long> getValues() { return values; }
   }
   ```
2. Obtain the `ObjectReader` for the class and retrieve the field reader for `"values"`.
3. Inspect `fieldReader.getItemType()` – it incorrectly reports `Object.class`.
4. Attempt to deserialize a JSON/JSONB input containing a list of numbers; the resulting list contains `Object` elements rather than `Long`.

**Expected behavior**
- `fieldReader.getItemType()` must return the concrete type argument of the `List` (e.g., `Long.class`) whether the bean has a custom constructor or not.
- Any `format`, `JSONSchema`, or other settings attached to the field must be properly honored when reading list items.
- All `FieldReaderListMethod` constructions (from `ObjectReaderCreator`) should consistently supply all available metadata (including `format` and `schema`) and allow the resolved `itemType` to be passed in, with correct fallback to generic inference when the caller does not provide an explicit type.

**Impact**
This prevents correct deserialization of many typical POJOs that combine constructor parameters with list setters, especially when using JSONB or when the code relies on exact item types for validation or serialization.


We need to implement proper cleanup of cached readers/writers in `ObjectWriterProvider` and `ObjectReaderProvider`. The current `cleanUp` methods leave stale state in internal reflection caches (fields, methods, constructors) stored in `BeanUtils`, so calling `cleanUp` on a class or classloader does not actually force the providers to recreate a fresh `ObjectWriter`/`ObjectReader` for that class – they end up returning the same instance from the cache.

## Desired behavior
- Rename the existing `cleanUp` methods to `cleanup` (lowercase) for consistency with Java naming conventions.
- `cleanup(Class objectClass)` must also evict all cached reflection metadata (fields, methods, constructors) for that specific class, so that the next `getObjectWriter` / `getObjectReader` call for that class produces a new instance and re-reads the class metadata.
- `cleanup(ClassLoader classLoader)` must evict all cached reflection metadata for all classes loaded by that classloader in addition to the existing provider-level cache eviction.
- These methods must remain safe to call with `null` arguments and should not throw exceptions.

## Why this matters
Without fully clearing the reflection caches, callers cannot reliably force re-initialization of serialization/deserialization metadata after class reloading or dynamic class modifications (e.g., inside containers, test environments, or classloader-sensitive applications).

## Where to look
- Provider cleanup logic: `ObjectWriterProvider` and `ObjectReaderProvider`
- Reflection caches: `BeanUtils` holds concurrent maps for field, method, and constructor metadata. These need to be cleared in the same cleanup paths.
- Existing tests for `Issue783` (which currently uses `cleanUp`) will need to be updated to match the new method name and must confirm that after cleanup, a new reader/writer instance is created.

ObjectWriterProvider & ObjectReaderProvider support cleanUp, for issue #783


Issue: On Android, serializing a class that extends a parent and contains a field with the same name (overriding) can produce incorrect output: the parent's field may appear instead of the child's, or both may be written, leading to duplicate keys or wrong values. Additionally, enums with certain annotations cause serialization to fail or crash on Android, likely because the optimized enum writer does not handle those annotations. We need to fix the writer creation/selection logic so that the correct field writer is chosen and enums with annotations are handled gracefully across all writer creators (default, ASM, Lambda).

Tasks:

1. **Field writer selection ordering for inherited fields.** When building the object writer for a class, fields are collected from both declared fields and getters. If both parent and child provide a field writer for the same name, the current `putIfAbsent` logic may keep the first one encountered, which could be the parent's instead of the child's. We need to ensure that when there is a collision, the "better" writer (as defined by `FieldWriter.compareTo`) wins and replaces the existing entry. This should apply to both field-based and method-based writers. The expected behavior is that for a class like `C2 extends P2` where both define `id`, the resulting writer should contain only one field writer, and its `field.getDeclaringClass()` should be `C2.class`.

2. **Format propagation for getter-based fields.** Currently when iterating getters to create field writers, the `beanInfo.format` is not propagated to the `fieldInfo`. This may cause formatting information to be lost, affecting serialization correctness (e.g., date formats). We need to set `fieldInfo.format = beanInfo.format` before processing each getter, similar to how features are set.

3. **Enum writer creation with annotations.** The optimized enum writer classes (`FIeldWriterEnumField`, `FieldWriterEnumMethod`, `FieldWriterEnumFunc`) are used when no custom `enumValueField` is present. On Android, if the enum class has annotation names (as returned by `BeanUtils.getEnumAnnotationNames`), these specialized writers may not handle the annotations correctly and can cause issues. We should guard the creation of these writers: if `BeanUtils.getEnumAnnotationNames(fieldClass)` is not null, fall back to the generic object/bean writer path instead of using the optimized enum writer. This check needs to be added in three places:
   - when creating a writer from a field (around where `FIeldWriterEnumField` is returned),
   - when creating a writer from a method (around `FieldWriterEnumMethod`),
   - when creating a writer from a function (around `FieldWriterEnumFunc`).

All changes should be made in `ObjectWriterCreator` (or its overrides) so that the behavior is consistent across the default, ASM, and Lambda implementations. Existing tests for field ordering (`TimeSortTest.test2`) should be updated to directly test the creators rather than relying on the default writer provider to ensure coverage of all three creator types.


Fix the JSON parser so that `JSON.parseArray` throws a `JSONException` when it encounters a malformed object inside the array where an array or object is used as a field name. For example, the input `"[{[]}]"` should immediately produce a `JSONException` rather than being silently accepted or throwing a different error. The fix must ensure that any similar invalid structure—where an object element of an array contains a field name token that begins with `{` or `[` and does not correctly transition to a colon—is consistently rejected with a `JSONException`.


When multiple identical JSONPath strings are provided in the same `JSONPath.of(String[], Type[])` call, extraction against the typed multi‑path produces incorrect results. For example, `JSONPath.of(new String[]{"$.c1", "$.c1"}, new Type[]{Long.class, String.class})` applied to `{"c1":123}` should return both positions populated with values (each possibly coerced to the corresponding type), but currently one or both positions in the result array end up null.

The specialized typed‑multi extractors (`JSONPathTypedMultiNames` and friends) appear not to handle duplicate paths correctly. A correct implementation must detect when the paths array contains duplicates and, when it does, fall back to a path‑by‑path evaluation strategy that correctly fills every output position, including duplicate positions.

This applies to single‑segment name paths (`$.c1`), multi‑segment name paths (`$.a.b.c1`), and array‑index paths (`$[0]`). The fix must also preserve the existing fast‑path specializations when all paths are distinct.

fix jsonpath multi path duplicate cause result error


When deserializing JSON into a Kotlin data class that declares default values for its constructor parameters, missing fields in the JSON input should cause the Kotlin default to be used. Currently this works unreliably for primitive types. For example, given:

```kotlin
data class Meta(var id: Int = 1, var tag: String = "json")
```

Parsing `{}` should produce `Meta(id=1, tag="json")`, but instead a `NullPointerException` is thrown. The issue appears only when a primitive-typed parameter (e.g. `Int`) is omitted from the JSON; reference-typed parameters seem unaffected. The same problem occurs when only *some* fields are supplied—for instance `{"tag":"kraity"}` should use `id = 1` but also fails.

The request is to make deserialization of Kotlin classes with default parameters robust for primitive types, so that:

- Missing primitive fields receive the Kotlin default value (not a Java zero-value, and not an exception).
- The correct Kotlin synthetic “marker” constructor (the one with the int bitmask and `DefaultConstructorMarker`) is reliably identified and used for invocation, even when the class has multiple constructors.
- The fix should not break existing handling of reference-type default parameters or change the behavior for classes without default parameters.

perf: improve the default-param of Kotlin's primitive type


**Title:** `@JSONField(format)` not applied when converting a bean to a `JSONObject`

**Description**
When a Java bean is converted to a `JSONObject` (e.g. via `JSONObject.from(bean)` or the underlying `ObjectWriterAdapter.toJSONObject`), temporal fields annotated with `@JSONField(format = "…")` are ignored — the field value appears in its default form (e.g. a `long` timestamp or ISO-8601 string) rather than as a string formatted according to the annotation.

*Expected behavior:*
For any field of type `java.util.Date`, `java.time.LocalDate`, or `java.time.LocalDateTime` that carries `@JSONField(format = "…")`, the corresponding value in the resulting `JSONObject` must be a string formatted using the given pattern. The formatter must be applied exactly as it is when serialising to a JSON text via `JSON.toJSONString`.

*Affected surface:*
Any API that ultimately uses `ObjectWriterAdapter.toJSONObject` (including `JSONObject.from` and `JSON.toJSON`) should exhibit the formatting behaviour.

**Acceptance criteria**
1. A bean with `@JSONField(format = "yyyy-MM-dd")` on a `Date` field produces `"2023-07-25"` (not a timestamp) when the bean is converted to a `JSONObject`.
2. Same for `LocalDate` and `LocalDateTime` fields.
3. Fields without `format` (or with `null` format) are unaffected.
4. Null values remain `null`.
5. The change does not alter the behaviour of `JSON.toJSONString` or `JSONB` serialisation.


**Title:** @JSONField name override not consistently applied when multiple getters/setters map to the same JSON name

**Description**

When a class has multiple properties whose getter/setter methods produce the same JSON field name — typically because one property uses `@JSONField(name = "…")` to alias to another property's name — the serialization and deserialization behavior is non-deterministic or incorrect. The library may pick the non-annotated property instead of the explicitly annotated one.

Concrete scenario:
```java
public class Bean {
    private String address;                // getAddress / setAddress
    @JSONField(name = "address")
    private String newAddress;             // getNewAddress / setNewAddress
}
```
Both fields contribute a JSON field named `"address"`. The user intention is that `newAddress` (the annotated field) should be used for reading and writing the `"address"` JSON property. Currently this is not guaranteed; sometimes `address` wins.

**Expected behavior**
- When multiple `FieldReader` / `FieldWriter` candidates exist for the same JSON field name, the library must give priority to the one whose associated setter/getter method **name** (after stripping the bean prefix: `set`/`get`/`is`) matches the configured `fieldName` (which respects `@JSONField(name)`).
- This priority must work symmetrically for both **deserialization** (readers) and **serialization** (writers).
- The fix must not break cases where the method name already matches without annotations, or where only one candidate exists.

**Non‑obvious edge cases**
- Both fields might have the same method name after stripping the prefix (e.g., two overloads). In that case, existing parameter-type priority rules should still apply *first*, and the new rule should only disambiguate when the method base names differ.
- The `fieldName` of a reader/writer is already set according to annotations and naming strategy. The comparison logic already in `FieldReader.compareTo` and `FieldWriter.compareTo` should be extended to add this new precedence rule.

**Acceptance criteria**
1. Given the bean above, `JSON.parseObject("{\"address\":\"value\"}", Bean.class)` populates `newAddress` and not `address`.
2. Given the bean above with `newAddress` set, `JSON.toJSONString(bean)` produces `{"address":"value"}` using `newAddress` only.
3. The behavior is stable regardless of the order in which the JVM returns methods/fields.

fix JSONField priority for getter & setter


**Title:** `ClassLoader` properties are not ignored during serialization

**Description:**

When I serialize a Java object that exposes a `ClassLoader` instance (either via a public getter such as `getMyClassLoader()` or a direct public field of type `ClassLoader`), fastjson2 includes that property in the resulting JSON. For example, given:

```java
public class Bean {
    public ClassLoader getMyClassLoader() {
        return this.getClass().getClassLoader();
    }
}
```

calling `JSON.toJSONString(new Bean())` produces JSON that contains a `"myClassLoader"` key with the ClassLoader object serialized.

This is undesirable because `ClassLoader` instances are internal VM objects that are not intended for serialization and can cause issues like unexpected deep traversal or stack overflows. The library already silently ignores certain other system types (e.g., `InputStream` during field scanning).

**Expected behavior:**

Any getter/field whose type is `ClassLoader` (and not explicitly annotated to be included) should be ignored during serialization, just as other internal JVM types are already ignored. In the example above, `JSON.toJSONString(new Bean())` should produce `{}`.

This applies both to getter-discovered properties and to public fields whose type is `ClassLoader`.

ignore ClassLoader properties, for issue #3434


**Title:** fastjson1-compatible `JSONArray.getString` returns JSON-encoded representation for `Date` and other non-String element types

**Component:** `fastjson1-compatible` module (`com.alibaba.fastjson` package)

**Description:**
When an `com.alibaba.fastjson.JSONArray` contains elements that are instances of `java.util.Date` (or `java.time.temporal.TemporalAccessor`, `Boolean`, `Character`, `Number`, `UUID`, `Enum`), calling `JSONArray.getString(int index)` does not return a plain textual representation of the value. Instead it falls through to `com.alibaba.fastjson2.JSON.toJSONString(value)`, which produces a JSON-encoded string (e.g. a date becomes a quoted JSON string rather than a simple formatted date). This is inconsistent with how `com.alibaba.fastjson.JSONObject.getString(String key)` handles the same types, and also diverges from the behavior of `com.alibaba.fastjson2.JSONArray.getString(int index)` in the core module.

**Expected behavior:**
1. For `java.util.Date` elements, `getString` should return a formatted date-time string (e.g. `"2021-12-31 23:59:59"`) using the default zone and the same formatting rules applied by `JSONObject.getString` and the core libraries.
2. For elements that are instances of `Boolean`, `Character`, `Number`, `UUID`, `Enum`, or any `java.time.temporal.TemporalAccessor`, `getString` should return the result of `value.toString()`.
3. The method should continue to return `null` for `null` elements and return the element unchanged if it is already a `String`.

**Acceptance criteria:**
- `com.alibaba.fastjson.JSONArray.getString(int index)` returns a plain `String` representation (not a JSON-encoded string) for the element types listed above.
- The behaviour is aligned with `com.alibaba.fastjson.JSONObject.getString(String key)` in the same compatibility module, and with `com.alibaba.fastjson2.JSONArray.getString(int index)` in the core library.

fix JSONArray.getString with Date type, for issue #2596


I’d like to use a JSONPath filter expression like:

```
[?exists(@.Data.user_info.userid)]
```

to check whether a nested property path exists within a JSON object. Currently, fastjson2’s `exists` filter appears to accept only a single property name (e.g. `exists(@.Data)`). When I try a dotted multi‑name path, the filter isn’t recognized or doesn’t work as expected. Please add support for multi‑level dotted paths inside `exists(…)`.

**Desired behavior**
- `exists(@.A.B.C)` should evaluate to the current context object if walking `A → B → C` finds a value (the final property exists) and every intermediate node is a Map / JSON object. If any intermediate key is missing or any intermediate node is not a Map, the filter should return `null` (so no match).
- Single‑name `exists(@.key)` must continue to work as before.
- The dotted path should support arbitrary depth (e.g. `A.B`, `A.B.C.D`).
- This filter should work in the same places the current `exists` works, such as inside array filter expressions `[?exists(…)]`.

**Observable constraints**
- For a JSON object where the full path exists, `JSONPath.eval` should return the same object instance that matches (the container of the filter, typically the current array element or the root), matching the semantics of the already‑working single‑name exists.
- When the path does not exist (missing final key, a missing intermediate key, or an intermediate value that is not a Map), the filter should exclude that element / return `null`.
- The feature should not break any existing uses of `exists` with a single name.

Please implement the necessary parsing and evaluation support so that a dotted property path inside `exists(…)` is correctly tokenized and evaluated against nested maps.

jsonpath support multi-names exists filter


```
**Bug Report: JSONPath.remove and JSONPath.set do not handle nested objects/arrays consistently with JSONPath semantics**

**Observed behavior:**

Using `JSONPath.of("$.a.b")`:

1. When the root is a `JSONObject` like `{"a":{"b":"xxx"}}`, `path.remove(root)` leaves the key `"b"` present with a `null` value (`{"a":{"b":null}}`) instead of actually removing the entry (`{"a":{}}`).

2. When the parent `"a"` is a JSON array of objects (`{"a":[{"b":"xxx"}]}`), `path.remove(root)` and `path.set(root, value)` do not apply the operation to each element of the array. The result is not the expected `[{}]` after remove or `[{"b":value}]` after set.

3. Similar inconsistencies appear when `$.a.b` is applied to a Java bean where `a` is either a nested bean or a `List` of beans: `remove` may not clear the field, and `set` may not propagate into the list elements.

**Expected behavior:**

- `path.remove(root)` should actually remove the key from `Map` instances, not set the value to null.
- When the segment's parent is a `Collection` (array), `remove` and `set` should iterate over each element, performing the removal or set operation on each element. `null` elements in the collection should be safely skipped.
- For non-`Map` objects, `remove` should set the corresponding field to `null`. For `set`, the field should be updated.
- The behavior should be consistent whether the immediate parent is a map, a collection of maps, a bean, or a collection of beans.
- When `set` is used on a collection of maps, it should respect the `JSONReader.Feature.DuplicateKeyValueAsArray` feature if enabled.

**Impact:** `JSONPath.remove` and `JSONPath.set` give incorrect results for nested paths involving maps, arrays, and beans, limiting usability in dynamic data manipulation scenarios.
```


When `ReferenceDetection` is enabled during JSONB serialization, lists or maps containing duplicate values of boxed primitives (e.g., `Integer`) can produce incorrect output or throw exceptions, because the reference-detection logic incorrectly treats these types as reference-tracked objects.

The fix should ensure that types which are explicitly marked as **not reference-detectable** (via `ObjectWriterProvider.isNotReferenceDetect`) are excluded from reference tracking, similarly to how `EMPTY_LIST`/`EMPTY_SET` are already excluded. This must apply both in the general reference-path construction inside `JSONWriter` and in the list-item handling within `ObjectWriterImplList`, where the current logic incorrectly forces reference detection for list elements regardless of their actual detectability.


**Bug: DuplicateKeyValueAsArray loses accumulated values when a key appears more than twice**

When parsing JSON with `JSONReader.Feature.DuplicateKeyValueAsArray`, if a key occurs three or more times in the same object, the resulting value is not an array containing all the duplicate values. Instead, only the last value is retained – the accumulated array from earlier duplicates is overwritten.

**Steps to reproduce**
Parse the JSON string `{"a":0,"b":1,"b":2,"b":3}` using `JSON.parseObject(str, JSONReader.Feature.DuplicateKeyValueAsArray)` and inspect the value for key `"b"`.

**Actual behavior**
`jsonObject.get("b")` is the integer `3`, not an array. The value accumulated by the first duplicate pair (`1` and `2`) is lost.

**Expected behavior**
Key `"b"` should contain a `JSONArray` with elements `[1, 2, 3]`, preserving all values in order of occurrence. The same guarantee should hold for any number of duplicate keys beyond two, whether parsing into a `JSONObject`, a plain `Map`, or typed map structures. When the feature is active, the implementation must not overwrite an already-accumulated collection with a single subsequent value; instead it must keep adding to the same collection.

fix DuplicateKeyValueAsArray, for issue #1100


Fix autoType enforcement and type-resolving for objects that appear inside JSON arrays.

Right now, when you parse a JSON top-level array (e.g. `JSON.parseObject(str, Object.class, …)`), the `ErrorOnNotSupportAutoType` feature and any registered `AutoTypeBeforeHandler` (via `JSONReader.autoTypeFilter`) are not consistently applied to the objects *within* the array. As a result:

* **Missing rejection:** If `ErrorOnNotSupportAutoType` is active and the input contains an object with a `@type` field anywhere inside an array, the parse succeeds silently and returns a plain `JSONObject` / `Map` instead of throwing a `JSONException`.
* **Missing resolution:** When an `autoTypeFilter` is provided (and `ErrorOnNotSupportAutoType` is also active to enforce type filtering), objects inside arrays that carry a matching `@type` are still parsed as generic maps; the type discriminator is ignored and the filter is never consulted.

**Acceptance criteria**

1. If `JSONReader.Feature.ErrorOnNotSupportAutoType` is enabled, any JSON that contains a `@type` field in an object – including objects that appear as elements of a JSON array – must cause a `JSONException` to be thrown (unless the type is explicitly allowed by a configured handler).
2. When an `AutoTypeBeforeHandler` has been registered (for example through `JSONReader.autoTypeFilter`) and `ErrorOnNotSupportAutoType` is also set, parsing a JSON array whose elements carry a `@type` that is *accepted* by the filter must resolve those elements to the corresponding type (e.g. a Spring Security `SimpleGrantedAuthority`). Elements whose `@type` is *not* allowed must still cause an exception.
3. Both behaviors must work when parsing with `JSON.parseObject(…, Object.class, …)` (i.e. without specifying a concrete element type), which is the typical path for generic deserialization.

**Constraints**

* The `@type` field is conventionally the first field in an object. The fix does not need to handle pathological reordering, but a well-formed object with `@type` as its first field must be honoured.
* The patch must not break normal parsing of arrays containing plain objects that do *not* include a `@type` field.
* The change should remain compatible with the existing safety defaults (`SAFE_MODE` / `DENY` lists) and with the overall autoType architecture.


The `fastjson1-compatible` module exposes `com.alibaba.fastjson.JSONObject` and `com.alibaba.fastjson.JSONArray` as a v1 API, but internally delegates to fastjson2. When retrieving nested JSON containers through `get`, the returned values are sometimes raw `com.alibaba.fastjson2.JSONObject` or `com.alibaba.fastjson2.JSONArray` instances. This breaks clients that cast the result of `get` to the v1 types (causing `ClassCastException`). Both key-based retrieval on `JSONObject` and index-based retrieval on `JSONArray` should always wrap internal fastjson2 containers into their corresponding v1 wrapper objects before returning them.

fix issue #2856, improve fastjson 1.x compatibility


**Title:** `serializeUsing`/`deserializeUsing` ignored for private primitive fields accessed via getter/setter

**Description:**
When a private primitive field (`int`, `long`, `short`, `byte`, `float`, `double`) has explicit getter/setter methods and is annotated with `@JSONField(serializeUsing = ..., deserializeUsing = ...)`, the custom codec is silently ignored during both serialization and deserialization. The problem does **not** occur for public fields with direct field access, nor for non-primitive (Object) fields with getter/setter access.

Expected behaviour:
- For serialization: `JSON.toJSONString(bean)` should use the provided `ObjectWriter` implementation and produce the formatted output.
- For deserialization: `JSON.parseObject(str, Bean.class)` should use the provided `ObjectReader` implementation to convert the raw JSON value into the primitive field value.

**Scope / example:**
```java
public static class Bean {
    @JSONField(serializeUsing = MyCodec.class, deserializeUsing = MyCodec.class)
    private int value;
    public int getValue() { return value; }
    public void setValue(int v) { this.value = v; }
}
```
Serializing this bean should invoke `MyCodec#write(...)`, and parsing should invoke `MyCodec#readObject(...)` with the primitive field type. Currently neither happens.

**Acceptance criteria:**
- Custom serialization is honoured for all primitive field types listed above when the field has a getter.
- Custom deserialization is honoured for all those primitive field types when the field has a setter.
- The fix must not break existing behaviour for non-primitive fields or public fields.
- The custom reader/writer must be supplied with the correct field type (the primitive type, not its boxed equivalent) so branching inside the codec remains possible.

**Possible root cause hints (for investigation):**
- The reader for getter/setter fields may not be setting the `FieldInfo.READ_USING_MASK` feature bit correctly (the mask set for direct field access differs from the method‑based path).
- The writer likely fails to recognise that the value class observed at runtime (boxed wrapper) corresponds to the `initValueClass` (primitive type), causing the custom `ObjectWriter` never to be selected.

fix : primitive getter/setter JSONField#serializeUsing/deserializeUsing not work


**Bug report: JSONPath filter `?(@.field == null)` is not supported**

**Description**
I am trying to use a JSONPath filter expression to select objects where a specific field is `null`. For example, given the JSON:
```json
{"A": {"B": {"C": null, "D": 1}}}
```
and the path:
```
$.A.B[?(@.C==null)]
```
I expected the result to be `{"C":null,"D":1}` (i.e., the object where `C` is `null`). Instead, the library currently either throws a parsing/syntax error or returns an empty result.

This is valid JSONPath syntax used by many implementations, and it is essential for querying JSON data that contains explicit `null` values. The same issue applies to the equivalent `JSONPath.extract` and `JSONPath.eval` paths.

**Expected behavior**
- `[?(@.field == null)]` should match objects where `field` is present with value `null`.
- The filter should be parsed without error and evaluated correctly in all standard evaluation contexts.
- Behaviour when a field is absent from the object (i.e., missing key) is acceptable to treat as `null` as well, though at minimum explicit `null` values must match.

**Impact**
Without this, any JSONPath query that needs to filter on `null` fields is unusable, which blocks common data-filtering use cases.

jsonpath support filter [@.xxx is null], for issue #1369


```
Bug Report: FastjsonSockJsMessageCodec should respect FastJsonConfig writer features for SockJS encoding

The class `FastjsonSockJsMessageCodec` in the fastjson2 v1-compatibility module is used to encode/decode messages for Spring WebSocket SockJS frames. Currently its `encode` method always constructs a default `JSONWriter` (via `JSONWriter.of()`) and ignores any configuration that users might need for serialization behavior. This prevents control over JSON writer features such as pretty-printing, quoting/escaping behavior, or bean-to-array mode, all of which are straightforward to configure elsewhere in fastjson2 via `FastJsonConfig`.

In practice, this means that the encoded SockJS frame may not be interoperable with the de facto standard `Jackson2SockJsMessageCodec` under the same inputs, especially when strings contain special characters that require consistent JSON escaping. For example, the default writer may produce different escaping than what Spring's Jackson-based codec produces, causing clients that expect the Jackson-compatible frame format to break.

The requested change:
- Allow a `FastJsonConfig` instance to be set on `FastjsonSockJsMessageCodec` (similar to other fastjson2 components that support `FastJsonConfig`).
- When constructing the JSON writer for encoding, use the writer features from that `FastJsonConfig` (e.g., `JSONWriter.of(fastJsonConfig.getWriterFeatures())`).
- Ensure that the default configuration (i.e., a fresh `FastJsonConfig` with no explicit features set) yields output identical to what `Jackson2SockJsMessageCodec` produces for the same input arrays, thus maintaining backward compatibility and interoperability by default.

This would bring `FastjsonSockJsMessageCodec` in line with the rest of the fastjson2 configuration model and resolve any subtle mismatches in SockJS frame formatting.
```

refactor FastjsonSockJsMessageCodec package and testcase.


Issue: JSONPath should provide string-based set/remove static methods and support wildcard removal

Currently, `JSONPath` offers static `set(Object rootObject, String path, Object value)` and `remove(Object rootObject, String path)` that operate on already-parsed objects. There is no corresponding static method that accepts a raw JSON string directly — users must manually parse the string, call `set`/`remove`, and then serialize the result back to a string. This is inconvenient for many common use cases.

Additionally, the existing `set` method returns `void`, which makes chaining cumbersome. It should return the root object.

There is also a behavioural gap in `remove`: when a wildcard path like `$.*`, `$[*]`, or `$..*` selects all elements of a container (a JSON object or array), `remove` throws an `UnsupportedOperationException` instead of clearing the container. Clearing should be the expected behaviour — remove all matching entries.

Desired changes:

1. Add a static `set(String json, String path, Object value)` method that parses the input JSON string, applies the path to set the value, and returns the resulting JSON string.
2. Add a static `remove(String json, String path)` method that parses the input JSON string, removes the elements selected by the path, and returns the resulting JSON string.
3. Change the existing `set(Object rootObject, String path, Object value)` to return the root object so callers can chain or inspect the result easily.
4. Fix `remove` so that when the path resolves to a Map or Collection (e.g., via `*`, `[*]`, or other wildcard), it clears that container instead of throwing an exception.

This would make the API more fluent and complete, and align the behaviour of `remove` with user expectations when dealing with wildcard removals.

JSONPath add static method 'remove', JSONPath.set & JSONPath.remove support more syntax #161


```
Title: JSONWriterUTF8 writeFloat/writeDouble should respect WriteNonStringValueAsString

The feature `JSONWriter.Feature.WriteNonStringValueAsString` forces non‑string values to be serialized as JSON strings.
In `JSONWriterUTF8`, the methods that write float and double arrays already observe this feature, but the scalar
`writeFloat(float value)` and `writeDouble(double value)` methods currently ignore it and always produce bare
numeric literals (e.g. `1.0` instead of `"1.0"`).

Expected behaviour when the feature is enabled:
- `writeFloat(1.0f)` should emit `"1.0"` (double‑quoted).
- `writeDouble(2.0)` should emit `"2.0"`.
- The internal buffer capacity check must account for the two extra quote bytes, otherwise a
  `BufferOverflowException` / truncation may occur when the output buffer is exactly sized.

The change should be localised to the two scalar writer methods in `JSONWriterUTF8`.
Consistency with the array variants and with other `JSONWriter` implementations is the goal.
```


We need a fix for deserialization of `EnumMap`, specifically when the serialized data represents an *empty* map. The core problem is that `java.util.EnumMap` requires the enum key type to be supplied at construction time. When the data contains no entries, the common intermediate deserialization step (populating a regular `Map`, then converting it via `new EnumMap(map)`) cannot infer the key type and either throws an exception or produces an incorrect result.

This manifests in at least these two scenarios:

1. **Empty `EnumMap` as a bean property (JSON text)**
   Deserializing a bean like `{"enumMap":{}}` where the target property type is `Map<SomeEnum, Object>` should produce an empty `EnumMap<SomeEnum, Object>` (or at minimum a `Map` instance that does not later fail when treated as an `EnumMap`). Currently it may fail or return a different map type.

2. **Empty `EnumMap` in JSONB with auto-type**
   When serializing a bean containing an `EnumMap` field (even an empty one) with `JSONWriter.Feature.WriteClassName` and then deserializing with `JSONReader.Feature.SupportAutoType`, the round‑tripped empty `EnumMap` should preserve the fact that it is an `EnumMap` with the original key type. Right now the deserialization can lead to a runtime exception or wrong map type.

The underlying cause is that the map readers responsible for typed maps (where the key type is known from the generic signature) attempt to build the final `EnumMap` by passing an intermediate `Map` to `new EnumMap(map)`. When that intermediate map is empty, the enum class cannot be deduced. The fix should ensure that in the typed map reading path, when the target type is `EnumMap` and the intermediate map is empty, the `EnumMap` is constructed directly using the known key type (the enum class from the generic type information), bypassing the conversion from the empty map.

**Acceptance criteria**
- An empty JSON object `{}` deserialized as a field of type `Map<SomeEnum, Object>` produces an empty `EnumMap` when the runtime type is expected to be `EnumMap` (or at least does not throw and the map is functionally usable for enum keys).
- A bean containing an `EnumMap` field whose value is an empty `EnumMap` (e.g., `new EnumMap(TimeUnit.class)`) survives a JSONB round‑trip with `WriteClassName` and `SupportAutoType` enabled without error, and the deserialized field is again an empty `EnumMap` of the correct key type.
- The fix should not break non‑empty `EnumMap` deserialization, nor affect other map types (`HashMap`, `LinkedHashMap`, etc.).
- The solution should be backwards‑compatible and not introduce new public API.

**Where to look (hint)**
The relevant logic lives in the `ObjectReaderImplMap` and `ObjectReaderImplMapTyped` classes inside the `reader` package. The `ObjectReaderImplMap` already handles the case when the runtime map type is raw `EnumMap.class` (currently it constructs a `LinkedHashMap` as the intermediate type and then wraps it). The typed variant `ObjectReaderImplMapTyped` has access to the concrete key type (the enum class) but does not yet special‑case the empty map scenario.

deserialize support EnumMap, for issue #1679


## Bug: `ValueFilter` returning non-List value for a null `List` field is ignored when `WriteNulls` is enabled

When serialising a bean that has a `List` field whose value is `null`, and `JSONWriter.Feature.WriteNulls` is used together with a `ValueFilter` that converts `null` to a non-`List` value (e.g. an empty String `""`), the filter’s replacement is discarded and the field is serialised as an empty JSON array `[]` instead.

**Expected behaviour:** The field should be serialised using the writer appropriate for the value actually produced by the filter. In the scenario above the filter produces an empty `String`, so the output should contain the field with the string value `""`.

**Minimal reproducer:**
```java
public class Bean {
    public List<String> values;
}

ValueFilter filter = (object, name, value) -> value == null ? "" : value;
Bean bean = new Bean();
String json = JSON.toJSONString(bean, filter, JSONWriter.Feature.WriteNulls);
// Expected: {"values":""}
// Actual:   {"values":[]}
```

The root cause appears to be that the serialisation infrastructure eagerly reuses a cached list-writer for the field even when the actual value class (after the filter) is not a `List`. It should only reuse that writer when the actual value class is assignable to the field's declared type; otherwise it must obtain a writer based on the real value class so that the filter result is correctly honoured.


When a `Date` field is annotated with both a fastjson2 `@JSONField(format = …)` and a Jackson `@JsonFormat` that includes `shape = JsonFormat.Shape.STRING`, the serialized output ignores the custom pattern. Instead of producing a string formatted according to the pattern (for example `"2025/08/22 14:00:00"`), the field may be written as a plain number or lose the pattern-driven formatting.

**Expected behavior**
- If a pattern is explicitly provided (via `@JSONField` or `@JsonFormat`), serialization must use that pattern, even when `shape = STRING` is also present.
- If no pattern is specified, `shape = STRING` should still cause the value to be serialized as a string (using the library’s default date format), and `shape = NUMBER` should serialize as a numerical timestamp (epoch millis).
- The pattern must not be overridden by `shape = STRING` when both annotations are present. Only when the field has no explicit format should the shape determine the output mode.

**Context**
This affects the processing of Jackson’s `@JsonFormat` annotation inside `com.alibaba.fastjson2.util.BeanUtils`. The serialization of fields that carry both annotations should respect the pattern regardless of shape, aligning with the typical interpretation that an explicit pattern takes priority.


The `register(ObjectReader)` and `register(ObjectWriter)` methods on `ObjectReaderProvider` and `ObjectWriterProvider` currently return a boolean indicating whether an entry was newly added, but there is no way to find out what was previously registered, conditionally register only if absent, or remove a registration.

Extend both providers with:

- Change `register(Type, ObjectReader/ObjectWriter)` to return the previous registered instance (null if none was registered).
- Add `registerIfAbsent(Type, ObjectReader/ObjectWriter)` that registers the instance only if no registration currently exists for that type, returning the existing instance (or null if none).
- Add `unregisterObjectReader(Type)` / `unregister(Type)` to remove by type, returning the removed instance or null.
- Add `unregisterObjectReader(Type, ObjectReader)` / `unregister(Type, ObjectWriter)` to remove a specific reader/writer association, returning true if it matched and was removed.
- Add `unregister(ObjectReaderModule)` / `unregister(ObjectWriterModule)` to remove a previously registered module.

Expose the new methods (`registerIfAbsent` for readers and writers) through the `JSON` facade class, and update the facade's existing `register` methods to match the new return types.

Additionally, fix a potential `NullPointerException` in `ObjectReaderProvider`'s static initialization when `AUTO_TYPE_ACCEPT_LIST` is null.

Update `fastjson1-compatible` module's `SerializeConfig.put` to adapt to the changed `register` return type (it should still return a boolean indicating whether a new entry was added).


When using JSONPath wildcard `*` on nested arrays, the results are incorrect.
For example, given the JSON string `"[[{\"a\": 1}]]"`:

- `JSONPath.extract(raw, "$[*][*]")` returns `[1]` instead of `[{\"a\":1}]`.
- `JSONPath.extract(raw, "$[0][*]")` returns `[1]` instead of `[{\"a\":1}]`.

The problem also appears with `eval`.
Parsing the same array and evaluating `"$[*][*]"` or `"$[0][*]"` also returns `[1]` rather than the expected inner object.

The expected behaviour is that a terminal `*` applied to an array element that is another array (or collection) should produce the elements of that inner array, not unwrap them further into the values of contained maps.

More generally, for any JSON structure like `[[...objects...]]`, the result of `$[*][*]` should be an array of those objects, not an array of their map values. The same applies to expressions like `$[0][*]` and `$[1][*]` (when the inner array contains objects).

This affects both the streaming `extract` path and the in-memory `eval` path. Please fix the wildcard handling so that terminal `*` correctly returns the elements of inner collections/arrays without prematurely flattening map values.

fix jsonpath extract '*', for issue #1070


In the `fastjson1-compatible` compatibility layer, the `SerializeWriter` constructors that accept `SerializerFeature` options are building the internal `JSONWriter` context with `JSON.DEFAULT_PARSER_FEATURE` as the base feature mask. This causes serialization defaults (for example, whether map null values are written, how circular references are handled, etc.) to deviate from the expected v1-compatible serializer behavior, because the writer is inheriting parser-default flags rather than generator-default flags.

Change the `SerializeWriter` constructors so they use `JSON.DEFAULT_GENERATE_FEATURE` as the base feature mask when creating the write context. The per-call `SerializerFeature...` varargs should still be applied on top of that base mask. No other serialization path should be affected.


Allow a single JSON string value to be deserialized into a `List<String>` field as a single-element list, instead of requiring a JSON array.
Currently, `{"books": "西游记"}` into `List<String> books` either throws a `JSONException` or produces an empty/null list.
After the change, the resulting list should contain exactly one element: the parsed string.

Requirements:
- This behaviour applies only to `List<String>`. For other `List<T>` with a non-String item type the existing mapping/skipping rules stay unchanged.
- The single string may be a quoted value (`"` or `'`).
- The field may be populated via constructor, field injection, or setter; all should produce a one-element list.
- Regular array input (`["a","b"]`) must continue to work as before.
- No other parsing paths or exception messages should be affected.

List<String> support single string input


The CSV writer does not escape fields that contain a comma, newline, or carriage return, producing invalid CSV output. For example, writing the string `,abc` via `writeString(byte[])` results in the unquoted text `,abc` instead of the properly quoted `",abc"`. This breaks parsers because the comma is interpreted as a column separator.

**Expected behavior**
- Any field value that contains a comma (`,`), a newline (`\n`), or a carriage return (`\r`) must be enclosed in double quotes.
- Fields containing an existing quote character (`"`) must continue to be escaped as before; this change does not alter double-quote handling.
- The behavior should be identical across both the UTF-8 and UTF-16 writer implementations.

**Acceptance criteria**
- `writeString(byte[])` (and any equivalent `String`-based write paths that share the same logic) applies quoting when the value contains a comma, line feed, or carriage return.
- Fields containing only a comma, with no double-quote characters, are still quoted.
- Output remains valid CSV: any column value that requires quoting is wrapped in a pair of double quotes.
- The fix is present in both `CSVWriterUTF8` and `CSVWriterUTF16`.

fix write csv escape comma & line separator


**Title:** support File read & write (#312)

**Description:**
`java.io.File` is not currently handled by fastjson2’s default serialization/deserialization. Types like `URI`, `URL`, `Class`, and `Type` are already supported by mapping them to/from a string representation; `File` should follow the same pattern.

Expected behavior:

- Serialization: a `File` value (top-level or nested) is written as its path string — the result of `toString()` (i.e. `file.getPath()`).
- Deserialization: a string value is converted to a `File` via `new File(string)`. `null` input should stay `null`.
- Must work with both the JSON and JSONB codec paths (i.e. `JSON.toJSONString`, `JSON.parseObject`, `JSONB.toBytes`, `JSONB.parseObject`, and all higher-level APIs).
- Should work when `File` appears as a field of any object, not just when parsing a standalone file string.

No special format customization or extra features are required — just the basic round-trip: `new File("/path").toString()` ↔ `/path`.


**Bug Report: `JSON.parseArray` fails when the array contains `null` and the element type is an `enum`**

When deserializing a JSON array whose items include `null` into a `List` of an enum type using `JSON.parseArray(String, Class)`, the library throws an exception instead of producing a list where those positions hold `null`.

**Steps to reproduce** (simplified):

```java
String json = "[null]";
List<MyEnum> list = JSON.parseArray(json, MyEnum.class); // throws or returns an unhelpful error
```

The problem occurs with any enum, including simple enums and enums that hold custom fields (e.g. a `code`/`desc` pair).

**Expected behavior**
The resulting `list` should contain `null` for each JSON `null` value, just as it does when the target element type is a regular class or a generic `Object`. For example, `[null]` should parse to a list with one `null` entry, and round-tripping through `JSON.toJSONString` should preserve `[null]`.

**Actual behavior**
An exception is thrown (e.g. a `JSONException` related to enum parsing), or the parsing fails entirely.

**Impact / context**
- This breaks deserialization of JSON payloads where enum fields may legitimately be absent / null, such as configuration arrays or optional filter lists.
- It also creates an inconsistency: null handling works for most other types but is missing for enum elements inside arrays.

The fix should ensure that null JSON values are mapped to `null` references in the resulting list when the declared element type is an enum, without changing the behaviour for non-null enum values or other types.

fix parseArray for Enum, for issue #1184


**Title:** `DirectByteBuffer` and read-only `ByteBuffer` serialization throws `UnsupportedOperationException`

**Description:**
Calling `JSON.toJSONString(buffer)` on a `java.nio.ByteBuffer` that is either direct (created via `ByteBuffer.allocateDirect()`) or read-only (obtained via `ByteBuffer.asReadOnlyBuffer()`) fails with an `UnsupportedOperationException`. The exception originates from an internal call to `ByteBuffer.array()`, which is not allowed on those buffer types.

**Expected behavior:**
Serialization should work for any `ByteBuffer`, not only for heap-backed writable buffers. The output should be the same byte-array representation (e.g., `[1,2,3]`) that a regular heap `ByteBuffer` produces.

**Additional constraints:**
- The serialization process must not mutate the buffer’s position. After `JSON.toJSONString(buffer)`, the buffer’s position should be the same as before the call.
- The fix should correctly handle all buffer subtypes (plain heap, heap read-only, direct, direct read-only, and mapped buffers) without special-casing internal JDK class names where possible.

**Steps to reproduce:**
```java
ByteBuffer direct = ByteBuffer.allocateDirect(3);
direct.put(new byte[]{1,2,3}).flip();
JSON.toJSONString(direct);   // throws UnsupportedOperationException

ByteBuffer ro = ByteBuffer.wrap(new byte[]{1,2,3}).asReadOnlyBuffer();
JSON.toJSONString(ro);       // throws UnsupportedOperationException
```

fix DirectByteBuffer java.nio.ByteBuffer#array UnsupportedOperationException (#3894)


**Title:** `JSON.toJSON` returns `JSONObject` with null fields by default, inconsistent with `JSON.toJSONString`

**Components:** `JSON.toJSON`, `ObjectWriterAdapter.toJSONObject`, `JSONWriter.Feature.WriteNulls`

**Description**

When serializing a Java object via `JSON.toJSON()`, the resulting `JSONObject` retains keys for properties whose values are `null`. For example, an object with a single `null` field and a non-null field will produce a `JSONObject` that still contains the `null`-valued key when it shouldn't.

This is inconsistent with `JSON.toJSONString()`, which correctly omits null-valued fields by default unless the `JSONWriter.Feature.WriteNulls` flag is explicitly set. The expected contract is:

- **Without** `WriteNulls`: null fields should be excluded from the output.
- **With** `WriteNulls`: null fields should be included.

The current behavior breaks that contract for `JSON.toJSON()` and any code path that produces a `JSONObject` from a Java bean.

**Expected behavior**

- `JSON.toJSON(object)` (and overloads) should skip fields whose values are `null` when `WriteNulls` is **not** active.
- When `WriteNulls` **is** active (whether set globally or passed as a vararg to the method), null fields should be present in the resulting `JSONObject`.
- The behavior must align with `toJSONString`, `toJSONBytes`, and stream-based writing methods that already honor `WriteNulls` correctly.

**Acceptance criteria**

1. Calling `JSON.toJSON(bean)` on a bean with a mixture of null and non-null fields returns a `JSONObject` containing **only** the non-null fields (i.e., `obj.containsKey("nullField") == false`).
2. Passing `JSONWriter.Feature.WriteNulls` (or having it enabled globally) causes those null fields to appear in the `JSONObject`.
3. The fix should not break existing `JSON.toJSONString` behavior, which already handles the feature properly.
4. Edge cases: beans with all-null fields (should produce an empty `JSONObject` without `WriteNulls`), inheritance hierarchies, and fields that are excluded by filters should still respect the feature.

fix JSON.toJSON JSONWriter.Feature.WriteNulls, for issue #2003


```text
JSON Schema string validation currently ignores the `const` and `enum` keywords.
For example, a schema `{"type":"object","properties":{"name":{"type":"string","enum":["Alice","Bob"]}}}` incorrectly accepts `{"name":"Charlie"}` as valid.
The same problem occurs with `const`, e.g., `{"type":"string","const":"hello"}` should reject any string other than `"hello"`, but it does not.

Make string schema validation enforce these standard JSON Schema constraints:
- If `const` is present, the string value must equal exactly that value.
- If `enum` is present (as an array of strings), the string value must be one of the listed values.

Both checks should produce appropriate validation failure messages. The fix must work for top-level string schemas as well as string schemas nested inside object properties.
```

jsonschema support string const & enum, for issue #1367


**Title:** `@JSONField(deserializeUsing)` is not invoked for fields that have no setter

**Description:**
I have a Java class where some fields are annotated with `@JSONField(deserializeUsing = …)` to supply custom deserialization logic (an `ObjectReader` implementation). The class does **not** provide setter methods for those fields—either because the fields are `final`, or because the class is designed for constructor/builder-based assignment and deliberately omits setters.

When I parse a JSON string into this class, the custom `ObjectReader` is **never called**. Instead, the library tries to deserialize the raw JSON value directly as the field’s declared type, which fails or produces incorrect results. For example, if the field stores a `Map` but the JSON contains a JSON string that my custom reader needs to parse, fastjson2 ignores my `deserializeUsing` and attempts to convert the string to `Map` on its own.

**Expected behavior:**
The custom deserializer specified via `@JSONField(deserializeUsing)` should always be honoured, regardless of whether the target field has an accessible setter method. The read‑only nature of the field should not prevent the custom reader from being invoked; the library can then use reflection or unsafe to assign the resulting value to the field.

**Impact:**
This makes it impossible to use field‑level custom deserializers on immutable objects, classes that use field injection without setters, or any design where setters are intentionally absent. The same annotation works correctly when a setter exists, but fails silently for read‑only fields.

**Request:**
Please ensure that `deserializeUsing` on a field is respected even when no setter is present (e.g., for `final` fields or any field for which no corresponding setter method exists).

fix JSONField#deserializeUsing on Field, for issue #1249


```
Title: Gson adapter missing fromJson overloads for java.lang.reflect.Type and TypeToken

Description:
The Gson adapter class (com.alibaba.fastjson2.adapter.gson.Gson) currently
provides only `fromJson(String, Class<T>)`. This prevents deserialization of
generic types such as `List<Car>` that require capturing the full parameterized
type at runtime.  In Gson, users can call `fromJson(json, new TypeToken<List<Car>>(){})`
or `fromJson(json, (Type) Car.class)`.  Neither overload exists in the fastjson2
Gson adapter.

Expected behaviour:
1) Add a method `public <T> T fromJson(String json, java.lang.reflect.Type typeOfT)`
   that deserializes from the given string using the fastjson2 reader infrastructure,
   analogous to the existing `fromJson(String, Class)` path.

2) Provide a `TypeToken<T>` class in package `com.alibaba.fastjson2.adapter.gson.reflect`
   that captures generic type information.  Since fastjson2 already has a
   `com.alibaba.fastjson2.TypeReference<T>` that serves the same purpose
   (capturing the type via subclassing), the new `TypeToken` should extend
   `TypeReference<T>` and be otherwise minimal.  A protected no-arg constructor is
   sufficient.

3) Add a convenience overload `public <T> T fromJson(String json, TypeToken<T> typeToken)`
   that delegates to the new `fromJson(json, Type)` method using
   `typeToken.getType()`.

Additional notes / constraints:
- The new `fromJson(String, Type)` overload must behave consistently with the
  existing `Class`-based version: it should use the same reader provider,
  handle resolve tasks, and return the parsed object.
- The existing `fromJson(String, Class)` implementation can serve as a template
  for the new method.  The primary difference is the use of `Type` instead of
  `Class<T>` to look up the ObjectReader and pass the type during reading.
- No serialisation changes are required; this request is about deserialisation only.
- The `TypeToken` class should not break existing fastjson2 TypeReference usage;
  simply extending it is the natural integration.

Impact:
Without these additions, projects migrating from Gson to fastjson2 via the
adapter module cannot deserialize generic containers or use the common
`TypeToken` pattern, forcing them to rewrite or avoid the adapter entirely.
```


```
When using a `JSONPath` expression that begins with `$..` (the recursive descent operator) followed by a filter condition (e.g., `$..[?(@.dictId == '101')]`), the result array may contain duplicate entries for the same JSON node. This happens when the same node is reachable through multiple paths during the recursive traversal. The expected behavior is that each distinct node satisfying the filter condition appears exactly once in the extracted array.

Also verify that the behavior of `$..` and `$..*` without filter conditions is consistent: these variants should not produce duplicate entries for elements that are reachable via multiple recursion paths.

The fix should apply to the `JSONPath` extraction logic within the `com.alibaba.fastjson2` package and must not change the semantics of other `JSONPath` expressions that do not involve recursive descent.
```

fix duplicated result when unique condition specified for issue #1700


Using `JSON.parseObject` with the class of an anonymous subclass of a generic collection (e.g., `new ArrayList<String>(){}.getClass()`) does not correctly resolve the element type. The resulting list/map treats values as `Object` instead of the declared generic type, so parsing `"[123]"` into such an anonymous `ArrayList<String>` may produce `ArrayList<Integer>` (or fail later with a class-cast). The expected behavior is that the generic superclass type arguments should be used to infer the item type (for collections) or key/value types (for maps) when the provided class is an anonymous subclass with an empty simple name.

Please fix `ObjectReaderImplList` and `ObjectReaderImplMap` so that when the target `Class` has an empty simple name (as anonymous subclasses do), the reader factory unwraps to the superclass and its generic superclass before determining the component types. The same logic should apply to common collection/map implementations like `ArrayList`, `LinkedList`, `HashMap`, and `TreeMap`.


```
Title: Optimize digitN utility parsing functions for speed

Background:
IOUtils contains a family of digit parsing helpers: digit1 through digit8 for both byte[] and char[] inputs. These are heavily used during JSON number parsing. The current implementations use per-character access with getChar/getByte and combine results via shifts or helper calls (e.g., digit4 calls digit2). This style adds method-call overhead and misses opportunities for bulk reads and simpler arithmetic.

Request:
Refactor digit2, digit3, digit4, digit5, digit6, digit7, and digit8 for byte[] and char[] inputs to achieve higher throughput.

Requirements:
- Preserve exact contract: return the integer value of the leading `n` ASCII decimal digits ('0'–'9') as if they formed a decimal number (most significant digit first), or return -1 if any of the characters is not a valid digit.
- Avoid per-character method invocations inside the digit functions. Use bulk little-endian reads (e.g., getIntLE, getLongLE, etc.) and process all digits in a single validation-and-extraction step where possible.
- Validation should use efficient bitwise arithmetic (e.g., packed range checks) rather than per-character comparison.
- The final integer computation should be performed via a series of multiply-by-10-and-add steps, without large precomputed multiplier constants (like 10000000) or repeated shifts.
- All public overloads (digit2(byte[]/char[]), digit3(...), ..., digit8(...)) must be covered. Private helpers (digit2(int), digit3(int), digit4(int)) should be updated to match the new internal style for consistency.
- Char[] variants must handle Java characters that are still ASCII digits correctly, but should fail with -1 if any character code exceeds 0x7f (i.e., not a Latin-1 digit).
- The byte[] variants assume input bytes are ISO-8859-1 encoded digits; non-digit bytes must cause -1.
- No functional regression: all existing callers must continue to work identically. The change is purely an optimization; existing semantics (including -1 for any invalid input) must be preserved exactly.

Out of scope:
- Changing public signatures, adding new methods, or optimizing digit1.
- Performance measurement scaffolding; the goal is a cleaner, faster implementation that still passes existing tests (once updated to exercise the new code paths).
```


**Bug Report: `IgnoreAutoTypeNotMatch` not honored during auto-type deserialization with `SupportAutoType`**

**Scenario:**
When deserializing JSON/JSONB data that was serialized with `JSONWriter.Feature.WriteClassName` (i.e., contains a type identifier), the deserialization is performed with `JSONReader.Feature.SupportAutoType` enabled to allow auto-type resolution. The expected target class passed by the caller does **not** match the class name embedded in the serialized data. The intention is to use `JSONReader.Feature.IgnoreAutoTypeNotMatch` to silently fall back to the expected (caller‑provided) type instead of failing.

**Expected behavior:**
When `JSONReader.Feature.IgnoreAutoTypeNotMatch` is set, and the auto‑type resolved from the data is not assignable to the expected type, deserialization should **ignore the mismatch** and use the reader for the expected type. No exception should be thrown, and the resulting object should be an instance of the expected type.

**Observed behavior:**
Two problems occur:

1. **Exception on type mismatch:** Even with `IgnoreAutoTypeNotMatch` active, an exception is thrown indicating that the auto‑type does not match the expected type. The mismatch is not being tolerated as the feature intends.

2. **Reader‑cache pollution:** When the resolved auto‑type **does not** match the expected type, the reader for the auto‑type class is registered in the internal lookup cache **under the type‑name hash of the original serialized type**. This means future lookups for that type name can return the wrong reader, even when `IgnoreAutoTypeNotMatch` is not set, causing subtle mis‑deserialization for other call sites that share the same `ObjectReaderProvider`.

**Affected API surface:**
- `JSONReader.Feature.IgnoreAutoTypeNotMatch` combined with `JSONReader.Feature.SupportAutoType`
- `JSONB.parseObject(byte[], Type, JSONReader.Feature...)` flavors (and equivalent JSON text parse methods)
- Writing side uses `JSONWriter.Feature.WriteClassName` (or equivalent `JSONWriter.Context` with that feature) to embed the type.

**Reproduction outline:**
- Define two unrelated or incompatible bean classes (e.g., `Bean` and `Bean1`), both having a field like `id` (same field name/type so a successful read could produce a useful result).
- Serialize a `Bean` instance to JSONB with `WriteClassName`.
- Attempt to parse those bytes as `Bean1` with `SupportAutoType` and `IgnoreAutoTypeNotMatch`.
- Observe that an exception is thrown even though `IgnoreAutoTypeNotMatch` was requested.
- Even if the first problem is circumvented, subsequent parsing operations that rely on the cached auto‑type reader may return instances of the wrong type.

**Impact:**
Users cannot leverage `IgnoreAutoTypeNotMatch` to gracefully handle type mismatches in auto‑type enabled pipelines. This blocks use‑cases where the serialized shape carries a full class name (e.g., for polymorphic root or legacy data) but the caller wants to read it into a more generic or versioned class when the original class is unavailable or not wanted. The cache pollution can also cause hard‑to‑diagnose bugs in long‑running applications.

fix : deserialize IgnoreAutoTypeNotMatch support


```
Title: Jackson @JsonFormat(shape = STRING) ignored during serialization

Description:
When a field is annotated with `@com.fasterxml.jackson.annotation.JsonFormat(shape = JsonFormat.Shape.STRING)`, fastjson2 should serialize that field's value as a JSON string. Currently, the annotation's `shape` attribute is not processed, so a `Long` (or other numeric) field annotated this way is written as a JSON number instead of a quoted string.

Expected behavior:
- For a bean like:
  ```java
  public class Bean {
      @JsonFormat(shape = JsonFormat.Shape.STRING)
      private Long id;
      // getter/setter
  }
  ```
  `JSON.toJSONString(new Bean() {{ setId(1001L); }})` should produce `{"id":"1001"}`.
- The same behavior should hold with other writer features (e.g. `UseSingleQuotes` → `{'id':'1001'}`).
- The change should be equivalent to having `JSONWriter.Feature.WriteNonStringValueAsString` set on that field during serialization.
- Existing handling of browser-compatible long overflow (values outside the safe integer range written as strings) must not regress: when both conditions apply, the field remains a string.
- The serialization of other numeric types should also respect this annotation shape if they are wired through the same non-string-as-string feature.

Implementation note:
`@JsonFormat` is already partially parsed in `BeanUtils.processJacksonJsonFormat`. The `shape` property is not yet handled and needs to be recognized and translated into the appropriate `JSONWriter.Feature` mask on the `FieldInfo` of that field.
```

support Jackson Annotation JsonFormat#sharp=STRING, for issue #1167


I'm encountering a bug when deserializing JSON into a Kotlin data class that has more than 32 constructor parameters. With 33 or more fields, parsing either throws an IndexOutOfBoundsException or silently produces objects with incorrect property values (default values used where JSON-provided values should appear, or vice versa).

**Steps to reproduce:**
1. Define a Kotlin data class with >32 fields, each having a default value (e.g., `val field1: String = "", ... val field33: String = ""`).
2. Parse a JSON string that provides values for some fields beyond field32 but omits others.
3. The resulting object does not respect the JSON values correctly – for example, field33 and above may always take the default value, or an exception is thrown during construction.

**Expected behavior:**
The parser should correctly handle Kotlin data classes with any number of constructor parameters, matching JSON keys to fields using the default-value mask mechanism. Missing fields should fall back to their defaults, while present fields should use the parsed values. The fix should partition the mask into multiple 32-bit segments so that fields beyond the 32nd parameter are correctly tracked.

This issue makes it impossible to use fastjson2 with large Kotlin data models (e.g., API responses with many optional fields). The problem appears to be specific to the Kotlin data class constructor instantiation path.

fix: parsing failed when field exceeds 32 (#1490)


```
Title: Multi-name JSONPath with array index prefix returns incorrect/null results for non‑zero indices

Description
When creating a typed multi‑name JSONPath via `JSONPath.of(String[] paths, Type[] types)` where all paths share the same array‑index prefix (e.g. `"$[1].id"`, `"$[1].name"`), the resulting `JSONPath` does not correctly skip preceding elements of the input JSON array. It silently fails to navigate to the targeted element, which can cause `extract()` to return `null` or an array with missing/invalid values even when the input contains the expected data.

Steps to reproduce
1. Build a typed multi‑name path:
   `JSONPath path = JSONPath.of(new String[]{"$[1].id", "$[1].name"}, new Type[]{Long.class, String.class});`
2. Prepare a JSON array where the element at index 0 is `null` and the element at index 1 is an object with the target fields, e.g.
   `[null, {"id": 1001, "name": "test"}]`
3. Call `path.extract(jsonString)` or `path.eval(arrayObject)`.

Expected behaviour
- The path should skip past the first element and extract the fields from the second element, returning an `Object[]` like `[1001, "test"]`.
- If the input is a JSON literal `null`, the result should be `null`.
- If the element at the requested index is `null`, the result should be `null`.

Actual behaviour
- For index `0` the extraction may partially work, but for indices greater than `0` the result is `null` or an array containing nulls.
- `null` inputs and `null` targeted elements are not propagated as `null` results consistently.

Impact
Typed multi‑name paths with array‑index prefixes cannot be used on real‑world JSON arrays where the desired element is not the very first one, making this feature unreliable for non‑trivial payloads.
```


Title: IgnoreNoneSerializable does not skip non‑Serializable runtime values and fails in JSONB mode

## Description
When serializing with `JSONWriter.Feature.IgnoreNoneSerializable` enabled, the library correctly skips fields whose **declared type** is not `Serializable`. However, if the declared type is `Serializable` but the actual runtime value is a non‑`Serializable` subclass or anonymous instance, the field is still emitted, causing serialization errors. This happens frequently on Android where some framework implementations do not implement `Serializable` even though their interface/base type does.

In addition, the `IgnoredNoneSerializable` feature is not respected when writing in JSONB format; non‑serializable objects may still be written or trigger unhandled exceptions instead of being silently omitted.

## Expected behavior
1. When `IgnoreNoneSerializable` is active, **any** field whose runtime value is not an `instanceof java.io.Serializable` should be skipped completely, regardless of the declared field type.
2. JSONB output should honor `IgnoreNoneSerializable` identically to the text JSON path, filtering out non‑serializable nested objects rather than failing.
3. Error reporting for `ErrorOnNoneSerializable` should still include the relevant field or object context (the exact message wording may be adjusted, but should remain informative).

## Impact
Makes `IgnoreNoneSerializable` unreliable on Android, where many framework types are not `Serializable` but the user has no way to prevent them from being serialized. The feature’s contract (“skip any value that is not serializable”) is violated for runtime subclasses and in JSONB mode.


When converting a `JSONObject` to a Java bean via `to(Class)`, fields whose value is a `JSONArray` are not correctly converted to the target array type (e.g., `String[]`). Instead, the array field remains `null` or fails.

Additionally, calling `to(String.class)` on a `JSONObject` or `JSONArray` does not return the JSON string representation; it should return the same result as `toString()`.

The fix should ensure:
- `JSONObject.to(SomeBean.class)` where `SomeBean` has an array-typed field (like `String[]`) correctly converts the corresponding `JSONArray` value to that array type.
- `JSONObject.to(String.class)` and `JSONArray.to(String.class)` return the JSON string.
- No existing conversion paths (primitive wrappers, nested objects) are broken.


**Implementation request**

The JSON deserializer now incorrectly handles cases where multiple bean properties are mapped to the same JSON field name.
Specifically, when two or more fields (or setter methods) are annotated with `@JSONField(name = "...")` or end up with the same effective name, only *one* of them receives the value during deserialization; the others remain `null`.

This is a regression – it worked correctly in 2.0.25.

Expected behavior:
If a JSON key matches multiple property definitions of the target class, *all* matching properties should be set to the parsed value.
This must work for fields, setter methods, and mixed field/method mappings, while preserving the existing field-reader ordering and deduplication guarantees for the normal (one-name-one-property) case.

enhance support distinct field with the same name for issue #1567


```
Title: Parsing illegal JSON array with typed elements can loop endlessly or cause OutOfMemoryError

When using JSON.parseArray to parse an array into a list of a specific type, or into a tuple
with multiple declared types, certain malformed inputs cause the parser to enter an infinite
loop or to keep allocating objects until an OutOfMemoryError.  This is a security and
reliability problem because even a tiny input can bring down a server.

Examples:

  // single element type
  JSON.parseArray("[jia]", int.class);
  JSON.parseArray("[jia]", Integer.class);
  JSON.parseArray("[jia]", long.class);

  // multiple element types (tuple)
  JSON.parseArray("[jia]", Long.class, Integer.class);

In all these cases the input contains illegal tokens that cannot be converted to the expected
types.  Rather than throwing a JSONException immediately, the parser either loops forever
(re-reading the same token) or starts growing an unbounded list until the JVM runs out of
memory.  A related problem occurs when the number of elements does not match the number of
declared types:

  // too few elements
  JSON.parseArray("[]", Long.class, Integer.class);
  JSON.parseArray("[1]", Long.class, Integer.class);

  // too many elements
  JSON.parseArray("[1,2,3]", Long.class, Integer.class);

These should also be detected and reported as JSON exceptions, not silently processed or
allowed to loop.

Expected behaviour:

- All the calls above must throw a JSONException (not an Error, not an OOM).
- The exception should be thrown as soon as the illegal token is encountered, without
  creating multiple objects or looping.
- When a fixed number of types is provided, the parser must reject arrays that contain
  fewer or more elements than the declared types.

The root cause appears to be in the array-reading helpers (readArray / readList) that parse
into typed lists or fixed-size arrays: they do not check whether the reader is making
progress on each iteration, and they do not enforce element-count contracts when multiple
target types are specified.
```

fix: illegal array to cause OOM for #1717


## Task: fastjson2 – JSONPath extraction returns wrong value after skipping nested array elements

**Severity:** bug
**Affected component:** `com.alibaba.fastjson2.JSONPath` (extraction via `extract(JSONReader)`) + UTF‑16 reader `skipValue`

### Problem description
When a JSON document contains arrays of objects that themselves hold nested arrays, extracting a field that appears *after* such an array with `JSONPath.extract(JSONReader.of(jsonString))` can return `null` or an unexpected value instead of the correct content. The issue is reproducible with the UTF‑16 reader path only.

### Root cause hypothesis
`skipValue()` is used by the extraction logic to jump over parts of the document that are not on the path being evaluated. Inside an array, when a value is skipped and the next character is `,` (comma), the reader’s internal post‑value state is not correctly updated – the reader does not know that a comma separator has been consumed. Later, when the reader tries to advance to the next key‑value pair (e.g., the target field after the array), it misinterprets the stream and either overshoots or fails to recognise the key, leading to a wrong extraction result.

### Steps to reproduce
1. Construct a JSON string similar to the pattern below (the exact structure matters: multiple array elements, each containing at least one non‑trivial nested value, followed by a top‑level field that is the extraction target):
   ```json
   {
     "data": {
       "list": [
         { "id": 0, "values": [1,2,3] },
         { "id": 1, "values": [4,5,6,7] }
       ]
     },
     "status": "ok"
   }
   ```
2. Build a path `$.status` and call `path.extract(JSONReader.of(json))`.
3. **Expected:** `"ok"`
   **Actual:** `null` (or a different value, e.g. the reader may throw or return a partial result).

### Required behaviour
After `skipValue()` has consumed a value that is immediately followed by a comma inside an array, the reader must properly record that a comma separator was encountered. This means the `comma` flag (inherited from `JSONReader`) must be set to `true` before advancing past the comma character. This flag is later checked when reading field names and determines whether a separating comma is expected; without it the parser loses synchronisation with the input.

### Implementation notes
- The fix should be minimal and only affect the UTF‑16 reader’s `skipValue()` implementation (`JSONReaderUTF16`).
- The state update must be performed for every array element that is followed by a comma, including after skipping nested objects, arrays, strings, numbers, and literals. Make sure the change does not affect behaviour when the value is the last element (no trailing comma) or when the array is empty.
- Verify that existing tests still pass, and that the new extraction scenario works for more complex nesting (e.g., objects that contain empty arrays, arrays with a single element, etc.).
- There is no need to change the abstract `skipValue()` contract, the option reader, or the JSONB paths.


When evaluating a wildcard JSONPath expression such as `data[*].title`, null values in the matching elements are silently discarded from the result collection. This makes it impossible to distinguish between missing keys and keys explicitly set to null.

Add support for preserving null values through a new `JSONPath.Feature` flag – for example, `KeepNullValue`. When this flag is set, wildcard projections should include null entries rather than skipping them. The default behavior (nulls dropped) must remain unchanged so that existing callers are not affected.

Concretely, given:

```json
{
  "data": [
    {"title": "title_1"},
    {"title": null},
    {"title": "title_3"}
  ]
}
```

- `JSONPath.of("data[*].title", Collection.class).eval(...)` returns `["title_1", "title_3"]` (no null).
- Constructing the same path with the new `KeepNullValue` feature should return `["title_1", null, "title_3"]`.

Also ensure the typed `JSONPath.of(String path, Type type, Feature... features)` overload is available so callers can specify the feature alongside a target type.

support JSONPath.Feature.KeepNullValue, for issue #1159


**Title:** Incorrect XOR verifier for empty passwords and missing zero-padding in `password` attribute of XSSF sheet/workbook protection

**Description:**
When protecting an XSSF sheet or workbook with an **empty password** (i.e. protection is enabled but no password is required to remove it), the stored `password` attribute contains an incorrect XOR verifier value. The OOXML specification (and the binary Excel precedent) expects the verifier to be `0` when the password is empty; currently a non‑zero constant is written.

Additionally, the `password` attribute must conform to the `ST_UnsignedShortHex` type: exactly four uppercase hexadecimal digits. The existing helper writes the hash using `Integer.toHexString`, which omits leading zeros for values like `0xFF` (producing `"FF"` instead of `"00FF"`). This produces schema‑invalid XML and can cause interoperability problems with other tools.

**Acceptance criteria / expected behaviour:**
1. When `protectSheet("")` or `protectWorkbook("")` is called, the XOR verifier used in the legacy‑protected XML element must be `0`.
2. The `password` attribute on the protection element must always be a 4‑digit uppercase hex string, zero‑padded as necessary (e.g. `"0000"`, `"00FF"`, `"1234"`).
3. Passing `null` to `protectSheet`/`protectWorkbook` should still **remove** protection entirely (no regression).

Fix zero-padding and handling of empty passwords (meaning protection on, but no password to remove it) for XSSF workbook protection.


```
Title: DAYS360 returns wrong result for US (NASD) method when start date is normalized to 30

The DAYS360 function in formula evaluation (method = FALSE) produces incorrect
day counts in cases where the start date is a day that the US/NASD method
normalizes to 30 (for example, the last day of February) and the end date is a
31st.

Example:
  =DAYS360(DATE(2018,2,28), DATE(2018,3,31), FALSE)

Expected result: 30  (as Excel returns)
Actual result:   31

The issue is in the end-date adjustment rule: when the end date falls on the
31st, the method decides whether to roll to the 1st of the next month or to set
the day to 30 based on whether the start day is less than 30.  Currently this
decision uses the *raw* day-of-month of the start date, but it should use the
start day *after* applying the start-date normalization (capping to 30 and
treating last-day-of-month as 30).  For a start date like 2018-02-28, the
normalized day is 30, not 28, so the condition “start day < 30” should be
false, and the end date should stay at 30.

Please fix the DAYS360 implementation so that the comparison of the start day
against 30 is performed on the already-normalized start date, in accordance with
the DAYS360 US (NASD) method specification.
```

Bug 60029: apply suggested fix and fix some IntelliJ warnings


**Title:** XOR encryption crashes with empty password (ArrayIndexOutOfBoundsException)

**Description:**
When I try to process an Excel file (XLS) that has XOR encryption and an **empty** password (password = ""), POI throws an `ArrayIndexOutOfBoundsException` from `org.apache.poi.poifs.crypt.CryptoFunctions.createXorArray1`. The same exception occurs when I attempt to create such a workbook programmatically with an empty password.

**Expected behavior:**
An empty password is valid according to the MS-OFFCRYPTO specification (section 2.5.343 XORObfuscation). The library should handle it without throwing an exception. Specifically:

- `CryptoFunctions.createXorKey1("")` should return `0`
- `CryptoFunctions.createXorVerifier1("")` should return `0`
- `CryptoFunctions.createXorArray1("")` should return a 16‑byte obfuscation array built from the full 16‑byte `PAD_ARRAY` (the spec says the obfuscation array is filled with the password bytes followed by the pad bytes; when the password is empty the whole pad is used)

**Current behavior:**
The code inside `createXorArray1` copies the password bytes into a 16‑byte obfuscation array and then tries to copy a portion of the pad array using a length of `PAD_ARRAY.length - passBytes.length + 1`. When `passBytes.length` is 0, that length becomes `PAD_ARRAY.length + 1`, which exceeds the pad array size and triggers an `ArrayIndexOutOfBoundsException`.

**Impact:**
Any operation on XOR‑protected files with an empty password fails completely. This includes both reading existing files and creating new ones. Since empty passwords are explicitly allowed by the file format, this is a bug.

**Additional notes:**
The fix should only change the empty‑password path and must not alter the behavior for non‑empty passwords. The implementation should still produce correct obfuscation arrays for all password lengths.

Do not fail if an empty password is provided


PERCENTRANK.EXC returns incorrect results in several cases. The rounding and interpolation logic in the PercentRank helper (used by both PERCENTRANK.EXC and PERCENTRANK.INC) does not match Excel’s precise behavior, leading to off-by-small-amount errors when a significance is requested or when the value does not exactly match a data point.

**Expected behavior (from Microsoft documentation and Excel):**
For a dataset of {1, 2, 3, 6, 6, 6, 7, 8, 9}:
- PERCENTRANK.EXC(A2:A10, 7) = 0.7
- PERCENTRANK.EXC(A2:A10, 5.43) = 0.381
- PERCENTRANK.EXC(A2:A10, 5.43, 1) = 0.3

For the PERCENTRANK.INC dataset {1,2,3,4,5,6,7,8,9,10}:
- PERCENTRANK.INC(A2:A11, 5, 5) should yield 0.58333 (currently it may round incorrectly at high significance).

**Root cause hints:**
1. The shared rounding method `PercentRank.round()` currently takes a single `RoundingMode` and applies it directly at the requested significance. Excel’s actual rounding for PERCENTRANK is a two‑stage process: first round to `significance+3` digits using HALF_UP, then truncate to `significance` digits using DOWN. This affects both PERCENTRANK.INC and PERCENTRANK.EXC.

2. The interpolation method `PercentRank.interpolate()` computes `rankDiff = aboveRank - belowRank` using `double` arithmetic, which can introduce floating‑point error. The subtraction should be performed precisely via `BigDecimal` (using `NumberToTextConverter` to avoid losing precision from the double representation).

3. When the value is not found in the dataset, the recursive rank calculations (`calculateRank`) for the closest‑below and closest‑above values must be performed at higher internal precision (significance + 3 at least) so that the subsequent interpolation is not truncated prematurely.

**Acceptance criteria:**
- PERCENTRANK.EXC returns exactly the values listed above for the given dataset and queries.
- PERCENTRANK.INC with a high significance (e.g., 5) matches Excel’s output.
- Both functions continue to pass their existing error‑case and boundary tests.

[bug-49202] add PERCENTRANK.EXC function


Title: NullPointerException in XSSFVMLDrawing when loading XLSX with empty VML part
Found in: poi-ooxml
Severity: medium – crashes on corrupted workbook

When opening certain corrupted `.xlsx` files with `XSSFWorkbook` a `NullPointerException` is thrown during loading. The file contains a VML drawing part (the part that stores comment shapes) whose XML content is effectively empty – the parsed `root` has no XML (e.g. a completely missing root element).

The exception occurs in `XSSFVMLDrawing.read()` when it tries to call `root.getXml().newCursor()`, because `root.getXml()` returns `null`. This causes the entire workbook open to fail, even though the rest of the workbook might be usable.

The expected behaviour is that an empty VML drawing stream should be handled gracefully: if the parsed XML document has no content (i.e., `getXml()` returns `null`), the drawing should be treated as empty and loading should proceed without throwing an exception. This is similar to how other parts in the library handle broken or missing content – they skip the invalid part and allow the rest of the workbook to be made available.

Reproducing the issue requires a specially crafted (fuzzed) `.xlsx` file where the VML drawing stream is corrupted to contain no actual XML. The same problem was identified by the poi-fuzz fuzzing infrastructure.

Please adjust the VML drawing loading logic to tolerate this condition without a NullPointerException.

Bug 66425: Avoid exceptions found via poi-fuzz