```
Feature Request: Search function to filter products by property existence

I need to filter products based on whether they have a product property with a given type code, language, and value. The current search capabilities don't support this directly. I would like a new search function `product:prop()` that can be used inside criteria expressions, similar to the existing `product:has()` function, but for product properties.

Acceptance criteria:

1. The function accepts three parameters:
   - `property_type` (string): the code of the property type (e.g., `"package-weight"`, `"color"`).
   - `language_code` (string|null): the language ID to match, or `null` to match only properties without a language.
   - `property_value` (mixed): the exact value of the property.

2. When used in a search expression via `$search->createFunction()`, it returns a non-null value (i.e., the matching property item ID) for products where a matching property exists, and `null` otherwise.
   - Typical usage: `$search->compare('!=', $search->createFunction('product:prop', [$type, $lang, $value]), null)` to require the property, or `== null` to require its absence.

3. Language matching must be exact:
   - If `language_code` is `null`: only properties where `langid` IS NULL match.
   - If `language_code` is a string (e.g., `"en"`): only properties with exactly that `langid` match.
   - No wildcard or fallback behavior.

4. Site isolation must be enforced, so only properties belonging to the current site(s) are considered. This must work the same way as `product:has()` and other built-in search functions.

5. The function should be available as a private (non-public) search attribute, like `product:has()`, so it can be used as a subquery criterion without exposing a new public filter field.

This would allow us to filter products like:
- "all products with property type 'color' = 'red' in German language"
- "products without a package-weight property (when no language is set)"

Using this function, we can build dynamic category listings based on arbitrary property combinations.
```

Implemented product:prop() search function


Please integrate alternative template engine support directly into the `MW\View\Standard` class instead of using the separate `MW\View\Compose` wrapper. The goal is to allow the standard view to dispatch rendering to different engines based on file extension, while keeping its own built-in PHP template rendering for extensions that have no registered engine.

### Background
Currently, supporting multiple template engines (e.g., `.php` and `.phtml`) requires constructing a `MW\View\Compose` object that holds separate view instances per engine. This is cumbersome and creates artificial separation. We want `MW\View\Standard` to be able to handle both `.php` templates via its existing `include`-based mechanism, and other engine-driven templates in a single view.

### Behavioral requirements
1. **Engine registration:**
   The `MW\View\Standard` constructor must accept an optional associative array mapping file extensions (including the dot, e.g., `'.phtml'`) to engine objects. The engine must conform to a new interface (let’s call it `MW\View\Engine\Iface`) that exposes a method `render(string $filepath, array $values): string`.

2. **Rendering dispatch:**
   When `render($template)` is called, the view first resolves the template file to an absolute path.
   - If the extension of the resolved file matches one of the registered engines, the engine’s `render` method is called with the resolved file path and **the whole assigned values array** (as a plain array, not the view object). The engine must not have access to the view’s helpers or magic `$this` properties – only the raw values array.
   - If no engine matches the extension, the view falls back to its standard PHP `include` mechanism, where values and helpers remain accessible via `$this` (exactly as before).

3. **Removal of `MW\View\Compose`:**
   The `MW\View\Compose` class should be deleted. All usages of it in production and test code must be replaced by the new direct engine support in `MW\View\Standard`.

4. **Backward compatibility for PHP templates:**
   Existing `.php` templates must continue to work without changes. They should still access assigned values via `$this->key` and call helpers like `$this->translate(...)`. The built-in `includeFile` mechanism must be preserved.

5. **Data scope for engines:**
   Engines must not reuse the view’s helper or value objects; they receive a plain associative array of values. This ensures that a template like `template.phtml` using `$values['quantity']` works correctly when rendered through a PHTML engine, while a `.php` template using `$this->quantity` and `$this->translate(...)` still works through the default path.

### Acceptance criteria
- Construct a `MW\View\Standard` with paths and a registered engine for `.phtml`. Assign a quantity value and render a `.phtml` template. The engine should receive the values array and produce output that uses only those values (no helpers or `$this`).
- With the same instance, render a `.php` template using `$this->quantity` and a view helper. It must work identically to the old behavior.
- The `MW\View\Compose` class is no longer present, and all existing tests for it are gone (replaced by tests covering the new engine dispatch inside `Standard`).

### Constraints
- Do not change the public API of `MW\View\Iface` (the main view interface) – the new engine interface is an internal contract used only by `Standard`.
- The resolution of template file paths (relative path lookup) must not be altered; it must remain reusable for both engine-based and PHP-based rendering.

Rewrites alternative view engine handling


Order status updates can produce duplicate entries when the same status type and value are reapplied after a different status value has been set for that type. The guard that prevents adding a redundant status entry should check for an existing entry with the **same value**, not merely any non‑empty entry of the same type. Apart from this guard, the behavior must stay the same: if no matching entry exists, a new status record is added; if a matching entry already exists, no duplicate is created.


We need infrastructure in the MW layer for running tasks in parallel when possible, falling back to sequential execution where pcntl is unavailable. Add a new `MW\Process` component with the following contract:

- An interface defining `isAvailable(): bool`, `start(\Closure $fcn, array $data, $restart = false): $this`, and `wait(): $this`.
- A PCNTL-backed implementation that uses `pcntl_fork` and respects a configurable max number of concurrent tasks (default 4). Child processes should run at a low priority (default 19). Install a `SIGTERM` handler that forwards the signal to all managed child PIDs and then exits. `start()` must block when the concurrency limit is reached until a slot opens; the child should set priority, call the closure, and exit with 1 on `\Exception`, 0 otherwise. `wait()` must drain all remaining tasks. After a child exits with a non‑zero status, throw a `MW\Process\Exception` unless `$restart` was true, in which case restart the task exactly once (with `$restart` false for the retry). All pcntl errors must result in a `MW\Process\Exception`.
- A decorator that checks `isAvailable()` and, if true, delegates to the inner object; otherwise, executes the closure synchronously on `start()` (via `call_user_func_array`) and makes `wait()` a no‑op.
- A dedicated exception class `MW\Process\Exception`.

Make sure the PCNTL implementation only reports `isAvailable()` when running on the CLI SAPI and `pcntl_fork` exists.

Implement infrastructure for processing tasks in parallel


### Allow selling events after their end date via configuration option

**Problem**
Products of type “event” are automatically hidden from the front-end catalog and cannot be purchased once their `date.end` has passed. This is too restrictive for merchants who sell past events (e.g. archived webinars) and want to keep them available for sale.

**Desired behavior**
Provide a boolean configuration option, e.g. `mshop/product/manager/standard/strict-events`, that controls whether events are hidden after their end date.

- When `true` (default): current behavior – events with a passed end date are considered unavailable and are hidden from the catalog and blocked at checkout.
- When `false`: event products remain visible in the catalog and can be added to the cart and purchased even after their end date. All other availability checks (status, date range for non-event types, deleted products) must continue to work as before.

**Additional context**
The `ProductGone` checkout plugin currently performs its own manual availability checks (status, date range, product existence) instead of relying on the product manager’s default availability logic. This causes inconsistency: even if the manager’s search is relaxed, the plugin may still reject the product. The plugin should use the same availability rules as the rest of the system so that the new configuration option is fully effective.

Adds configuration option to sell events that are over


**Request:** Fix product media CSV import to apply the mapped language ID column and allow clearing it on update.

**What’s wrong**
When a column in the product CSV is mapped to `media.languageid`, the value is not stored on the created or updated media items. Additionally, if an update row provides an empty string for that column, the previously set language ID is not cleared; it remains unchanged.

**Acceptance criteria**
- **Import with a non‑empty language ID**
  Given a CSV row where a column is mapped to `media.languageid` and contains a language code (e.g. `de`), the corresponding media item must have its language ID set to that value after import.
- **Update with an empty language ID**
  Given an existing media item that already has a language ID set, when a subsequent CSV update row maps the same `media.languageid` column to an empty string, the media item’s language ID must become null (i.e. cleared).
- **Consistency when the row yields multiple URLs**
  If the `media.url` column contains multiple URLs separated by the configured separator, each generated media item must receive the same language ID as the row provided. The language ID must not be lost or mis-assigned for any of the resulting items.

**Context**
The issue is within the product import CSV processor for media items. The processor reads mapped columns from the row and calls the media manager to create/update items. Currently, `media.languageid` is either ignored or lost during the per-URL iteration, and the update path does not treat an empty value as a clearing signal.

Fixed language ID import for media items


# Case-sensitive text search in product index

## Problem

Text searches against the product index are case-sensitive. A search for "cafe" does not match a product named "Cafe Noire Cappuccino", and a search for "NOIR" also fails. Users expect e-commerce search to be case-insensitive.

## Expected behavior

All text-based index searches (fulltext relevance, name search, and text value search) should match regardless of letter case. Both the query terms and the indexed content must be normalized so that "cafe", "Cafe", and "CAFE" all produce the same results.

## Affected area

This affects the index text manager, specifically:

- Fulltext `index.text:relevance` / `index.text.relevance` searches
- Name-based `index.text:name` searches
- `index.text.value` searches with custom type/language/field combinations
- The `searchTexts()` method that returns matching text snippets
- The text saving path that stores indexed content

The issue applies across all database backends (MySQL, PostgreSQL).

## Additional context

The index already handles text tokenization (splitting on punctuation, building search terms). The case normalization needs to happen consistently in both directions: when text is stored into the index, and when search query terms are processed. If only one side is normalized, mixed-case existing data or mixed-case queries could still miss matches.

Allow case insensitive search in index text manager


```
Title: Null comparisons are incorrectly turned into IS NULL for all operators

When building criteria expressions via `\Aimeos\MW\Criteria\Iface::compare()`, passing `null` as the comparison value currently causes the expression to always use `createNullTerm`, regardless of the operator.

For example:
- `compare('>=', 'mycol', null)` → produces `mycol IS NULL`
- `compare('>', 'mycol', null)` → produces `mycol IS NULL`
- `compare('~=', 'mycol', null)` → produces `mycol IS NULL`

This is semantically wrong: only equality (`==`) and inequality (`!=`) operators should produce null-specific terms (IS NULL / IS NOT NULL). For other operators a `null` value should not be silently reinterpreted as a null-check; it should either cause an error or follow the normal term construction path, but it must not generate `IS NULL`.

Expected behavior:
- `compare('==', 'col', null)` → `col IS NULL`
- `compare('!=', 'col', null)` → `col IS NOT NULL`
- For any other operator (`>`, `<`, `>=`, `<=`, `~=`, …) with a `null` value, the expression should not produce an IS NULL / IS NOT NULL clause.

The affected code lives in the abstract comparison expression base class (`\Aimeos\MW\Criteria\Expression\Compare\Base::toString()`), which both the SQL and PHP expression backends inherit from. The fix should restrict the null-term shortcut to the two operators where it is semantically correct.

Acceptance criteria:
- The SQL backend no longer generates `IS NULL` for operators other than `==` and `!=`.
- The PHP backend no longer generates equivalent null checks for those operators.
- Existing tests for `==`/`!=` with `null` remain passing.
- Tests that previously expected exceptions for invalid null+operator combinations are removed or updated to reflect the new behavior.
```

Create null terms only for allowed operators


Implement the following simplification for list-item handling across the common list-ref traits and catalog managers:

Currently, the `ListsRef` item trait maintains a separate `$listRefItems` property and a `prepareListItems()` method that attaches referenced domain items and sorts list items by position at read time. However, the lists manager already returns list items with their referenced items attached and sorted correctly. This duplication causes complexity and extra state.

**Requirements**
- Remove the `listRefItems` property and the `prepareListItems()` method from `Aimeos\MShop\Common\Item\ListsRef\Traits`.
- Change `initListItems(array $listItems, array $refItems)` so that `$listItems` is a flat, ID-keyed array (not domain-grouped), and internally reorganize them by domain. The `$refItems` parameter becomes optional and unused.
- In `Aimeos\MShop\Common\Manager\ListsRef\Traits::getListItems`, the returned collection must be ordered by list item position.
- In all manager code that builds items (`Catalog\Manager\Base::buildItems`, `Common\Manager\ListsRef\Traits::buildItems`, `Catalog\Manager\Standard::getTree`), stop building a separate `refItemMap`. Pass an empty array for the `$refItems` argument to `createItemBase` because the referenced domain items are already part of each list item.
- In `Catalog\Manager\Standard::getTree`, use the result of `getListItems` directly with a `groupBy('catalog.lists.parentid')` to obtain the per-node list items instead of manually grouping.
- Update any test fixtures that rely on the old `initListItems` signature to use the new flat-ID format.

The overall behavior for consumers of the item API (`getListItems`, `getRefItems`) must remain unchanged.


```
Title: Basket migration does not handle language/site changes and error reporting is insufficient

Affected component: Arcavias frontend basket controller

Observed behavior:
When a customer changes the locale (site, language, or currency) while items are in the basket, the basket migration only reacts to currency changes. Switching the site or language leaves the basket in an inconsistent state — for example, products from the old site or language remain, possibly with incorrect prices or unavailable items.

Additionally, when a migration step fails (e.g., a product is not available in the new locale), the error message only states how many products failed (“X products aren't available for the currency Y”) but does not indicate which products or other basket components (addresses, coupons, services) were affected. This makes debugging and user feedback very difficult.

Expected behavior:
- Locale-aware basket migration should detect changes to all locale dimensions: site, language, and currency.
- When a migration is triggered, basket components (products, addresses, coupons, services) that cannot be transferred to the new locale should be gracefully handled, with the rest migrated.
- Error reporting must include per-item details (e.g., which product, coupon, address type, service type failed and the reason) instead of a single opaque count.

Safety constraints:
- The new locale must be bootstrapped correctly (including site, language, currency) before attempting migration.
- Migration should be attempted on each basket part independently, catching exceptions and collecting error information without losing the overall migration result.
- The final exception thrown should carry all collected errors so that upstream code can inspect and display them properly.

Impact:
Without this, multi-language or multi-site shops experience broken basket states and poor error reporting after locale switches, undermining the dynamic locale switching feature of Arcavias.
```

Checks language for migrating the basket and improves error handling


**Title:** Escape curly braces `{` and `}` in the encoder output to prevent template injection

**Description:**
The view encoder (`\Aimeos\MW\View\Helper\Encoder\Standard`) currently escapes quotes and backticks but does not escape curly braces (`{`, `}`). When the encoded output is later passed to a template engine that uses `{` / `}` as delimiters (e.g., Twig, Smarty), an attacker could inject template syntax and trigger server-side code execution or information disclosure.

**Acceptance criteria:**
- `attr()`, `html()`, and `js()` must escape `{` as `{` and `}` as `}`, just like other special characters are replaced with their HTML character entities.
- The escaping must happen in addition to all existing escaping rules – no existing escapes may be removed or weakened.
- In the `js()` method, an apostrophe (`'`) must be escaped as `'` (the HTML entity) rather than as a backslash-escaped single quote. This makes the JS encoder consistent with the HTML encoding approach and avoids any risk of an unescaped literal `'` appearing in the output.
- The `xml()` and `url()` methods are not required to change; they are out of scope for this request.

**Security context:**
If a user-supplied value (e.g., a product name or customer review) contains `{{ ... }}` or similar template directives, and that value is later placed into a page alongside a template engine that evaluates curly-brace-delimited expressions, the engine may execute those directives. Escaping the braces at the encoder layer prevents this class of injection.

Escape { and } to avoid template injection


```markdown
Title: Imagick image adapter does not apply compression; tests fail without extension

Summary:
The `\Aimeos\MW\Media\Image\Imagick` adapter reads a configurable quality setting via the `$options` array but never passes it to Imagick when saving an image. As a result, all images are saved without compression, producing unnecessarily large files. Additionally, the adapter’s unit tests fail entirely when the Imagick PHP extension is not installed.

Acceptance criteria / expected behavior:

1. **Image compression on save**
   - In `Imagick::save()`, after setting the image format, the adapter must apply compression using Imagick’s compression API.
   - The compression value must be taken from the existing quality configuration (`$this->options['image'][<mime-subtype>]['quality']`) when present, or fall back to a default of `20`.
   - This must work for both output paths: saving directly to a file (when `$filename` is provided) and returning a blob (when `$filename` is `null`).

2. **Test suite should skip when Imagick is absent**
   - The test class `\Aimeos\MW\Media\Image\ImagickTest` must be skipped entirely when the native `\Imagick` class is not available (i.e., the PHP extension is not loaded).
   - Running the test suite in an environment without Imagick should report the tests as skipped rather than failing with errors.

Constraints / notes:
- Do not alter the image format selection logic; compression should be added as a separate step after the format is set.
- The existing quality option parsing (including per‑MIME‑type overrides) must be preserved.
- No other adapters (e.g., GD) are affected by this change.
```

Added compression and skip tests if Imagick class isn't available


As a developer working on templates, I often need to access deeply nested parameter values that were passed to the view, like `$this->param('list')->get('test')->get('key')` or manual nested array checks. I'd like a simpler way.

Add a new view helper named `value` that makes it easy to retrieve parameter values from the view using a slash-separated path. The helper should accept a key string (e.g. `"list/test/key"`) and an optional default value. It should return the value at that path if it exists, or the default (null if not provided) if any part of the path is missing. The path may optionally have leading or trailing slashes; those should be ignored.

The implementation should follow the existing view helper conventions of the project (interface + standard class), and integrate with the current view and parameter infrastructure without breaking anything.

Implemented value view helper to retrieve parameter values easier


The `Index\Manager\Standard` (and its base `DBBase`) claims to implement the product manager interface (since `Index\Manager\Iface` extends `Product\Manager\Iface`), but its `saveItem` and `deleteItems` methods behave incorrectly for real product management:

1. **`saveItem` with a new product item**
   When passing a product item that has no ID yet, the index manager currently tries to rebuild the index using `$item->getId()`, which is null, causing an exception or leaving the item unpersisted. The method should first save the item through the core product manager (which assigns a valid ID) and then rebuild the index, so that the item is properly stored and indexed.

2. **`deleteItems` semantics**
   The public `deleteItems` method only removes corresponding rows from the index sub-tables. It never deletes the actual product records from the database. As a result, calling `deleteItems` on the index manager leaves orphaned product data. To fulfill the product manager contract, `deleteItems` must also delete the product itself (by delegating to the product manager) while still ensuring the index entries are cleaned up. Any internal method that just clears index entries (without touching product data) should be a separate, non-public operation used only by the index rebuilding logic.

The expected behavior is that the index manager acts as a proper product manager: when you save or delete items through it, the underlying product data is stored or removed accordingly, and the search index stays consistent. This will allow downstream code to use the index manager as a drop-in for the product manager without having to call both managers separately.

Implemented product manager interface in index manager


**Title:** Add product rating storage, search, and update operation to the product manager

**Description:**
The product item (`MShop_Product_Item_Standard`) already exposes `getRating()` and `getRatings()` methods, but the product manager currently does not persist, index, or provide a way to modify these rating values. We need full rating support in the product manager:

1. **Schema** – The product database table must include `rating` (decimal, e.g. 4.00) and `ratings` (integer count) columns with appropriate defaults. Add relevant indexes (e.g. on siteid + rating) so that rating-based queries are efficient.

2. **Persistence & Search** – The product manager’s search SQL (both the `search` and `count` queries, ANSI and MySQL variants) must select `rating` and `ratings` and include them in the `GROUP BY` clause so that these fields are available when items are loaded. The product manager’s search configuration should also expose `product.rating` and `product.ratings` as internal (non-public) search attributes so they can be used in filter criteria.

3. **Update Operation** – The product manager must implement a dedicated `rate(string $id, string $rating, int $ratings)` method that updates the rating and ratings count directly in the database for a given product ID, without loading the full product item. The update must be scoped to the current site (using the locale’s site ID) and follow the same prepared‑statement pattern used by other single‑statement operations in the product manager. The method should return the manager instance for chaining.

The implementation should follow Aimeos conventions: configuration‑driven SQL with ANSI (and optionally MySQL) statements under the `mshop/product/manager/standard/rate` config key, proper database manager acquire/release, and alignment with the existing code that handles similar one‑off update operations. The product item itself does not need changes; it already has getters/setters for these values.

Implemented rating updates in product manager


We want to be able to chain `assign()` and `render()` calls on view objects. For example, it should be possible to write:

```php
$view->assign(['key' => $value])->render('template');
```

Right now, `assign()` returns `void`, so this fails. The method should return the view instance (`$this`) to support a fluent interface. The interface contract in `MW\View\Iface` should also document this return type.

Support fluent interface for assign() in views


**Title:** Missing `last-rule` configuration option in the Percent catalog rule provider

**Description:**
The Percent rule provider (`lib/mshoplib/src/MShop/Rule/Provider/Catalog/Percent.php`) does not expose the `last-rule` backend configuration option, even though this option is defined by the base rule provider and expected to be available across all rule providers. When retrieving the backend configuration via `getConfigBE()`, the returned list contains only the provider-specific `percent` entry. It should also include the base configuration entries – in particular `last-rule` – so that administrators can control whether a rule is the final one to be applied.

**Acceptance criteria:**
- Calling `getConfigBE()` on the Percent rule provider returns an array that includes both its own `percent` configuration and the base backend configuration options.
- The `last-rule` option (with its existing definition from the base provider) appears in the configuration array.
- The `checkConfigBE()` method of the same provider continues to work correctly and also reflects any additional base configuration options when validating input.


Please make the `fromArray()` and `toArray()` methods of the order base product item aware of the embedded price object’s properties, so that price data can be fully serialized and deserialized using the array exchange format.

Currently, calling `fromArray()` with price-related fields (value, costs, rebate, currency, tax rates, tax value, tax flag) has no effect on the internal price object, and `toArray()` does not include those fields in the returned array. This breaks round-tripping of order product data when using the array-based interface.

Acceptance criteria:

- **`toArray()`** should include the following price properties from the internal price object, using the existing key prefix `order.base.product.`:
  - `currencyid`
  - `price` (the net price value)
  - `costs`
  - `rebate`
  - `taxrates` (the map of tax rates by tax name, e.g., `['tax' => '20.00']`)
  - `taxvalue`
  - `taxflag`

- **`fromArray()`** should update the internal price object when any of these keys are present in the input array. The keys must match those produced by `toArray()`.

- The change must be backward compatible: fields that are already handled (e.g., `order.base.product.type`, `order.base.product.name`, …) must remain unchanged, and the `$private` parameter behavior must be preserved (price fields should be treated like the other public fields).

Allow managing order product price properties using fromArray()/toArray()


```
Feature Request: Allow filtering products by stock level

I need to filter products in my e-commerce platform based on their stock levels.
Currently, the product manager’s search criteria does not include any stock‑related
search keys. The stock manager provides search attributes like `stock.stocklevel`,
but these are not accessible when building a product search.

Expected behavior
------------------
When I call `getSearchAttributes(true)` on the product manager, I should see
stock search attributes (e.g. `stock.stocklevel`) in the returned list. I should
then be able to construct product search criteria that use conditions like:

  * `stock.stocklevel > 0`   – to get only in‑stock products
  * `stock.stocklevel IS NULL` – to get products with unlimited stock

The product search must correctly join the `mshop_stock` table when these
attributes are used (with alias `msto`, joined on `mpro.id = msto.prodid`),
and apply the condition without errors.

Why this matters
----------------
Without this ability, it’s impossible to list only available products, which is
a fundamental requirement for any shop frontend.
```


**Feature Request: Configurable domains for fulltext index text ingestion**

The fulltext product index rebuild currently hard-codes inclusion of texts from `attribute` items only. This makes it impossible to index texts from other product-associated domains (e.g., `media`, `price`, `text` groups) without modifying core code.

**Desired Behavior**
Introduce a configuration key, e.g. `mshop/index/manager/text/domains`, that accepts a list of domain names whose text items should be indexed.
The default value must be `['attribute']` to preserve the existing behavior.

When rebuilding the index, for each product the system should:
- Collect the associated items for every configured domain (via the product’s list references).
- Retrieve the full domain items for those referenced IDs, complete with their attached text list items.
- For each such domain item, index its `text` list items, preserving the associated list type and text type.
- As a fallback: if a domain item has no attached text of type `name`, the item’s own name/label should be indexed under the text type `name` and list type `default`.

The product’s own texts (including the code) should continue to be indexed as before.

**Impact**
This change allows store operators and integrators to add texts from additional domains (e.g., media descriptions, custom entities) to the fulltext index through configuration alone, without needing to override the index manager. It should be fully backward compatible when the new configuration is absent or set to its default.

Make texts indexed for domains in fulltext index configurable


## Implementation request

**Summary:** Make catalog rule application consistently propagate to all related products, regardless of which rule provider is active.

**Current behavior:**
Only the `Percent` provider applied a rule to a product’s related products (by recursion inside its own `update` method). Other providers such as `Taxrate` only acted on the product itself. This leads to related products missing rule changes when non-Percent rules are used.

**Desired behavior:**
When any catalog rule is applied to a product, all of that product’s related products must also receive the same rule application, so that bundles, recommendations, and other product associations consistently reflect active rules.

**Special handling for selection products:**
Selection products have a set of “default” related products (the selectable article variants). These default articles must be handled directly by the rule provider (e.g., direct price/tax update) rather than going through the full rule chain again, to prevent duplicate applications. The manager must therefore skip those default articles when propagating rules to related products for selection items, while still applying rules to all other related products.

**Implementation notes:**
- Modify the rule manager’s `apply()` method so that, after applying a rule to the original item, it also applies the same rule type to the item’s related products (except default selection articles).
- Remove the recursion that `Percent::update()` currently performs for non-default related products, since that responsibility moves to the manager. The percent provider should still update default selection articles directly.
- For `Taxrate`, similarly update the selection default articles directly in the provider, and rely on the manager for the broader propagation.
- The existing `testApplyRebate` test may become obsolete and can be removed once the new propagation is covered at the manager level.

**Acceptance criteria:**
- All catalog rule providers exhibit uniform propagation to non-default related products.
- Selection default articles continue to be handled directly by the provider without re-entering the rule chain.
- No other behavioral changes to existing rule logic.

Always apply rules to related products too


We're switching the LIKE expression escape character from backslash to underscore to avoid MySQL's non‑standard handling of backslashes inside LIKE patterns.
In class `\Aimeos\MW\Criteria\Expression\Compare\SQL`, the SQL generation for the `=~` and `~=` operators currently emits `ESCAPE '\\'` and escapes the special characters `%`, `_`, `[` by prepending a backslash. Change it to emit `ESCAPE '_'` and to escape those special characters by prepending an underscore instead of a backslash.
The legacy backslash‑escaping in the SQL statement parser (`\Aimeos\MW\DB\Statement\Base::getSqlParts()`) that was needed to handle backslash‑escaped quotes in SQL strings is no longer necessary and can be removed — the existing single‑quote handling is sufficient. The behavior after the change should be that `getSqlParts()` correctly splits SQL strings without the extra backslash‑processing step.

This change affects all generated SQL that uses the LIKE operators (`=~`, `~=`). Everything that previously produced `… LIKE '…' ESCAPE '\\'` should now produce `… LIKE '…' ESCAPE '_'` with `_%`, `__`, `_[` used to escape the special characters inside the LIKE pattern.


```
Title: Streamline product property type domain from "product/property" to "product"

Currently the default product property types (package-height, package-length, package-width,
package-weight) are defined with `domain = 'product/property'` in the setup data. This is
inconsistent with how other product-related type domains work – for example, attribute types
that reference a product use the bare domain string `'product'`. The product characteristic
property panel in the administration interface expects domain `'product'` when fetching
available types, while the seeded data gives them a different domain, causing mismatches.

We need to:

- Change the domain of all product property types in the default `lib/mshoplib/setup/default/data/type.php`
  from `'product/property'` to `'product'`.
- Provide a database migration task (following the existing `Setup\Task` pattern) that updates the
  `domain` column in the `mshop_product_property_type` table for existing installations, converting
  `'product/property'` to `'product'`. The migration must be idempotent and safe on fresh installs
  (where the table may not exist).
- Update the relevant admin interface test(s) so they use the new domain (`'product'`) when
  looking up product property types.
```

Streamlined domain of product property types


**Title:** `index.catalog:position` search function fails when optional parameters are omitted

**Description:**
The `index.catalog:position` search function (defined by the catalog index manager) accepts optional parameters: a list type code and one or more category IDs, as described in its label `'Product position in category, parameter([<list type code>,[<category IDs>]])'`.

When the caller omits one or both parameters—for example, calling `$search->createFunction( 'index.catalog:position', ['promotion'] )` (omitting category IDs) or `$search->createFunction( 'index.catalog:position', [] )` (omitting both)—the generated SQL contains a dangling `AND` keyword before the position column (`mindca."pos"`), leading to a syntax error from the database.

**Expected behavior:**
The function must produce valid SQL for every allowed combination of the optional parameters:

- Only list type code provided
- Only category IDs provided
- Both provided
- Neither provided

The query must remain correct and return the expected products (filtered by the supplied criteria, or unfiltered when parameters are absent).

**AC:**
- Calling `index.catalog:position` with a list type but no category IDs works without error.
- Calling `index.catalog:position` with an empty parameter array works without error.
- Pre-existing invocations (all parameters supplied) continue to work.
- The fix remains compatible with the `sort:index.catalog:position` variant.

Added tests for optional index.catalog:position parameters


**Implementation request: Remove communication abstraction from PayPalExpress payment provider**

The PayPalExpress payment provider relies on the `getCommunication()->transmit(...)` pattern from the `\Aimeos\MW\Communication\Iface` interface for all HTTP interactions. This indirection adds complexity, makes debugging harder, and ties the provider to an abstraction that is not strictly needed for a single-curler module. Tests are also infected: they use a custom `TestPayPalExpress` class that mimics the communication interface with a fragile rule-checking mechanism.

We want to decouple the provider from the communication abstraction and handle HTTP requests directly, improving portability and simplifying both the production code and the test infrastructure.

**Expected change:**

- Replace every call of the form `$this->getCommunication()->transmit($url, 'POST', $payload)` inside `PayPalExpress` with a call to a new internal method that uses PHP’s curl functions directly.
- The new method must:
  - Initialize a curl resource.
  - Set the URL, request method (always POST), the payload as POST fields, a connection timeout (e.g. 25 seconds), and `CURLOPT_RETURNTRANSFER` to `true`.
  - Enable SSL peer verification.
  - Execute the request and return the response body as a string.
  - Throw an `\Aimeos\MW\Communication\Exception` with a descriptive message if curl initialization fails, curl execution fails, or a curl error occurs.
  - Ensure the curl handle is always closed, even when an exception is thrown, by using a try/catch that calls `curl_close` in the error path.
- The production class must no longer depend on `\Aimeos\MW\Communication\Iface` for request transmission.

**Test considerations:**

- The test suite must be adapted so that the new internal method is mocked using PHPUnit’s mocking facilities, returning the canned response strings that the existing test logic expects.
- The helper class `TestPayPalExpress` (which implements `\Aimeos\MW\Communication\Iface`) should be removed from the test file entirely, as it will no longer be needed.
- All tests that currently set up communication rules via `addRule` should instead configure the mock of the new send method with the appropriate response.
- The overall test coverage and assertions (e.g., payment status checks, method arguments) must stay the same; only the communication seam changes.

Do not alter the business logic around payment processing, order status updates, or error handling—only the HTTP transport layer.

This request addresses a structural cleanup that pays off in maintainability and test simplicity.

Replaced communication class by direct curl calls


We need to remove the array-based `setProperties()` method from the property‑management interface and trait because the item‑side has no reliable way to construct concrete property objects from raw data. Property items must be fully composed by the caller and passed in as `Property\Iface` instances. While doing this, we also tighten up deletion tracking so items that are replaced or explicitly removed are correctly recorded for later persistence, and deletion works even when the item only has a synthetic key (no persisted ID).

## Acceptance criteria

1. **No `setProperties()` on property‑ref items**
   - The `setProperties(array $entries, $types = null)` method must be completely removed from `MShop\Common\Item\PropertyRef\Iface` and its implementation in `Traits`.
   - No internal array‑to‑object conversion happens inside the item—callers that need batch insertion should use a new `addPropertyItems(\Aimeos\MShop\Common\Item\Property\Iface[] $items)` convenience loop (which just delegates to `addPropertyItem` for each item).

2. **Deletion tracking on `setPropertyItems()`**
   - When the entire property set is replaced via `setPropertyItems()`, any previously‑attached property items that are not present in the new list must be moved into the “deleted” tracking collection (i.e., `getPropertyItemsDeleted()` must return them).
   - This ensures the manager can later persist removals instead of silently dropping the references.

3. **Delete by object matching, not only by identity**
   - `deletePropertyItem()` must be able to remove an item even when it has no real ID (i.e., when it is keyed by a synthetic string like `'_parentId_type_lang_value'`). The current implementation only compares object identity with `===` and misses items added without an ID.
   - Both the true‑ID key and the synthetic‑ID key must be checked, and the removed item must be added to `propRmItems` (the removed‑items tracker).
   - After removal the item must no longer appear in `getPropertyItems()`.

4. **Cleanup of the trait contract**
   - The abstract `getResourceType()` method used only by the old `setProperties()` must be removed from the trait, since it is no longer required. Items that need a resource type expose it on their own; it is not a requirement of the property‑ref contract.

## Notes for implementation

- The synthetic key construction `_' . $this->getId() . '_' . $type . '_' . $langId . '_' . $value` is already used in `addPropertyItem`; reuse the same logic in `deletePropertyItem`.
- The removal pass in `setPropertyItems()` should collect old items into a temporary list, then call `deletePropertyItems()` on the ones that are being discarded before swapping the active list.
- Behavior must remain consistent with the existing `getPropertyItem()`, `getProperties()`, and `getPropertyItemsDeleted()` contracts.


**Ticket: Allow extending `Str` class dynamically and overriding `slug()`**

**Background**
The `Aimeos\MW\Str` class provides static utility methods, including `slug()`, which transforms a string into a URL-friendly segment. Currently there is no way for extension authors to replace the slug-generation logic or to add entirely new static helper methods without patching the class directly. This forces invasive core changes for simple customisation needs.

**Request**
Add a mechanism to dynamically extend `Str` so that developers can:

1. **Override `slug()` behaviour** – register a custom callable that will be invoked when `Str::slug(...)` is called. The custom callable must receive the same arguments as the original method (`$str`, `$lang`, `$sep`) and its return value must be used as the slug. When *no* custom callable is registered, the existing built-in slug logic must execute unchanged – full backwards compatibility.

2. **Add arbitrary static methods** – the same mechanism should allow registering a callable for any method name (e.g. `'upper'`) that does not currently exist in `Str`. Calling that name statically on the class must invoke the registered callable. Calling an unregistered non-existent method must result in a clear error (e.g. `InvalidArgumentException`).

3. **Retrieve and replace registrations** – a single public static method should work both as a setter (when a closure is provided) and a getter (when called with only the method name). It should return the currently registered callable (or `null` if none). This allows checking whether an override is in place and resetting behaviour when needed.

**Acceptance criteria / observable behaviour**

- `Str::slug('a/b&c', 'en', '_')` → `'a_b_c'` (default behaviour, unchanged).
- After registering a custom `'slug'` callable, the same call produces the custom result, and the original transformation is bypassed.
- Registering a callable for a non-existent method name (e.g. `'upper'`) enables `Str::upper('abc')` → `'ABC'`; calling an unregistered name throws an exception.
- The registration method returns `null` when no callable is registered for a given name, and returns the previously registered callable after setting a new one.

The implementation must not break any existing public API of `Str` and must keep the class fully static (no instance state visible to callers).

Allows to extend the Str class dynamically and overwrite the slug() method


**Transfer custom item properties of any scalar type in `copyFrom()`**

When calling `copyFrom()` to populate one item (e.g., an order product, order service, or address) from another (e.g., a catalog product, service, or another address), custom properties that were set with `set('key', value)` are only transferred if the value is a string. Non-string scalar values such as integers, floats, or booleans are silently lost.

**Expected behavior:**
- After `copyFrom()`, any scalar custom property (int, float, bool) that was present in the source item should be available in the target item with the same value, not just strings.
- Properties whose keys contain a dot (`.`) should continue to be filtered out (unchanged existing behavior).
- The fix should not alter the transfer of standard named properties; the visible change is strictly the inclusion of non-string scalars in the custom-column logic.

**Acceptance criteria:**
1. Set a non-string scalar custom property on a source item (e.g., `$source->set('count', 5)` or `$source->set('active', true)`) and then call `$target->copyFrom($source)`. After the copy, `$target->get('count')` returns the same scalar value.
2. Existing string custom properties continue to be transferred.
3. Properties with a dot in the key name are still excluded.
4. The fix should apply to all item types that inherit from the common base item (products, services, addresses, etc.) without per-type changes.

Transfer custom item properties automatically


```
Title: Order base items cannot be serialized (serialize() fails)

Area: MShop Order Item Base (Standard, Base)
Severity: medium

Description:
Calling serialize() on an instance of \Aimeos\MShop\Order\Item\Base\Standard (or
any class inheriting from the abstract Base order item) currently fails – PHP
refuses to serialize the object, probably raising a serialization-related error.

This blocks any usage that relies on serialization, such as storing order items
in sessions, caching layers, or value-object copies. The Standard class extends
Base, and the problem is suspected to be connected with property visibility and
how __sleep() collects the serializable properties across the inheritance
chain (e.g. private fields in child hiding parent properties).

Expected behaviour:
serialize($item) must return a valid string. Unserialization must restore an
equivalent object without data loss. The serialized form should include all
relevant order data and respect the existing __sleep() logic.

Task:
- Investigate and fix the serialization failure.
- Ensure the fix works without breaking other order-base item behaviour.
- Add an automated test that verifies serialization succeeds (i.e. returns a
  string), to guard against regressions.
```

Workaround for serialize() problem in order base items


We need to support range expressions in the criteria system: a new comparison operator `-` that accepts a range value `"lower - upper"` and matches records where the field is `>= lower` and `< upper`.

## What must change
- **New operator `-`** – The `compare()` method (available on both the PHP and SQL criteria objects) must accept `-` as a valid operator.
- **Range semantics** – When the operator is `-`, the value string is split on `" - "` into two parts: a lower bound and an upper bound. The generated condition must be equivalent to `field >= lower AND field < upper`. Both the PHP (in-memory) and SQL (database) backends must implement this behavior.
- **Operator registration** – The list of supported compare operators (returned by `getOperators()` on a criteria object, and by the underlying expression classes) must include `-` for both the PHP and SQL implementations.

## Constraints / edge cases
- The lower bound is inclusive (`>=`) and the upper bound is exclusive (`<`).
- The split delimiter is exactly a space-dash-space (`" - "`).
- Existing operators and escaping rules must remain unchanged. The new operator should not interfere with LIKE, `=`, `!=`, `>`, `>=`, `<`, `<=`, or any other existing comparison.
- The feature must work consistently whether the criteria object is backed by PHP or SQL; the generated condition source for each backend must reflect the range semantics.


```
Title: Media deletion does not remove preview files from storage

Description:
When a media item is deleted through the media controller's `delete` operation, the associated preview file (the path returned by `getPreview()`) should be removed from the filesystem together with the main media file. The only exception is that previews pointing to the shared mime-type icon directory (configured via `controller/common/media/standard/mimeicon/directory`) must be preserved, because those icons are shared resources managed separately.

At the moment, preview files are almost never removed: the deletion logic only deletes a preview when its path is exactly equal to the mime icon directory string, which effectively means previews are never cleaned up. This leads to orphaned preview files accumulating in storage.

Acceptance Criteria:
- After a successful `delete` call, the filesystem no longer contains the preview file, unless that preview resides inside the mime icon directory.
- The main media file (from `getUrl()`) continues to be removed as before.
- If no mime icon directory is configured (empty string), all previews should be removed.
- Previews whose path starts with the configured mime icon directory (including subdirectories) must be left untouched.
```


Allow the `Memory` config decorator to merge arrays from the local in-memory configuration with arrays from the underlying file configuration, instead of replacing them outright.
Currently, when `get()` returns an array and both the local config and the wrapped config contain arrays for the same key, the local array replaces the file array completely, so keys present only in the file config are lost.
The expected behavior: if both values are arrays, merge them recursively, with local values taking precedence over file values. If only one of the values is an array, use that array as‑is. For non‑array values the local value should continue to overwrite the file value entirely.
Caching and the negative‑cache logic must still work correctly (i.e., returned values should be cached, and `null` results should still be tracked).

Allow local and file configuration to be merged


In the **catalog list items** component, add an optional **“Add to basket” button** for each product. The feature must be behind the configuration key `client/html/catalog/list/basket-add` (disabled by default).

Requirements:

- Each product in the list view should render a `<form>` that posts to the basket standard action, with hidden inputs for the product ID and quantity, plus a submit button.
- The list view must work for **selection products** (product type `select`) that have variant sub‑products.
  Variant attribute selectors (width, color, etc.) must be rendered inline inside the list item, exactly as they are on the catalog **detail page** basket area.
  Reuse the variant‑rendering logic already present in the detail page basket‑selection template so there is no duplication.
- The selection‑product dependencies (which sub‑product corresponds to which attribute combinations) must be correctly exposed per list item so that front‑end JavaScript can update prices and hidden fields accordingly.
- The output of the list items client can be **cached**. CSRF tokens inside the basket forms need to be replaced at runtime after cache retrieval.
  The existing CSRF replacement in `Base::replaceSection` currently only replaces **a single** marker; ensure it works correctly when **multiple CSRF placeholders** appear in the same page content (e.g., one per product in the list).

Implements PHP code for adding products to basket from product list


```
Task: Improve performance of fastjson 1.x compatible serialization methods.

Two specific overloads in the compatibility layer are slower than they should be:
- `JSON.toJSONString(Object, boolean prettyFormat)`
- `JSON.toJSONBytes(Object, SerializerFeature... features)`

Currently these methods go through a heavy code path that allocates unnecessary
`SerializeFilter` arrays and creates a writer context via the filter-aware wrapper.
They need to bypass that overhead and directly use the fastjson2 writer
infrastructure, while preserving the existing serialization contract exactly.

Constraints:
1. Output compatibility must be maintained:
   - Date format defaults to milliseconds (`"millis"`).
   - The default time zone is the JVM default. Only set it on the writer context
     when the user has explicitly modified `JSON.defaultTimeZone` away from the
     JVM default (avoid unnecessary zone configuration).
   - Circular reference detection must be enabled by default, unless the caller
     explicitly passes `SerializerFeature.DisableCircularReferenceDetect`.
2. Other overloads (those that accept `SerializeFilter` parameters) must not be
   changed. The optimization is only for the no-filter paths.
3. Serialization errors must be caught and re-thrown as `JSONException` with an
   appropriate error message, consistent with existing behavior.

The expected outcome is a noticeable speedup for these commonly-used methods
without any observable change in serialized output or error handling.
```

improve fastjson 1.x compatible performance


Add support for Jackson’s `@JsonProperty(index = …)` attribute so that serialized JSON field ordering matches Jackson’s ordering. Currently, fastjson2 already handles other `@JsonProperty` attributes (`value`, `required`, `access`) but ignores `index`. This causes a mismatch in serialized output order when a bean uses the annotation to control field order. The desired behavior: when a class has fields annotated with `@JsonProperty(index = N)`, `JSON.toJSONString(bean)` should produce a JSON string where the fields appear in ascending order of their index values, matching what Jackson’s `ObjectMapper.writeValueAsString(bean)` would produce. The default index of `-1` (meaning no explicit order) should be treated as “no ordering” and not affect the output order.

Acceptance criteria:
- A bean with multiple fields carrying distinct `@JsonProperty(index = …)` values serializes to JSON with fields ordered by the specified indices.
- Fields without an `index` attribute or with `index = -1` are not reordered relative to this mechanism.
- The implementation should be consistent with how other `@JsonProperty` attributes are already processed in the annotation introspector.

support jackson annotation JsonProperty#index


**Bug Report: `List<String>` field populated with one empty string element when JSON value is empty string**

When deserializing a Java bean that contains a property of type `List<String>`, a JSON input with an empty string as the value of that property (e.g., `"values": ""`) is currently deserialized into a list containing one element: the empty string `""`. The expected behavior is an empty list (`[]`).

**Steps to reproduce:**
1. Define a bean with a `List<String>` field, for example:
   ```java
   public class Bean {
       private List<String> values;
       // getters and setters
   }
   ```
2. Deserialize the JSON `{"values": ""}` into that bean using the library's standard parse methods or a manually created `ObjectReader`.
3. Inspect the resulting `values` list.

**Actual result:** the list contains one element, an empty string.
**Expected result:** the list is empty (size zero).

**Context & impact:**
- An empty string is not a meaningful list element. Treating it as a list with a single empty string element breaks downstream logic that assumes empty string would be treated as an empty list.
- This behavior is inconsistent with how other JSON libraries handle this edge case, and it causes data integrity problems in applications that rely on type-safe deserialization.
- The issue affects both the default reflection-based readers and the ASM-based generated readers, so a fix should cover all code paths that handle `List<String>` fields.

**Desired fix:**
When the library encounters an empty string value for a `List<String>` target type, it should skip adding the element and leave the list empty. The fix should not affect other list types (e.g., `List<Integer>`) and must remain compatible with existing null-handling behavior.

fix : input empty string as empty list, for issue #1320


```
Title: FieldBased deserialization throws NPE when @JSONType includes FieldBased and class lacks public fields for some properties

When a class is annotated with
  @JSONType(deserializeFeatures = {..., JSONReader.Feature.FieldBased})
and some of its properties are defined only via getter/setter methods (or are inherited
without a backing field in the annotated class), the ASM-based object reader generation
throws a NullPointerException because it unconditionally calls field.getModifiers() even
when no backing Field object exists.

Expected behavior: FieldBased should only imply direct-field writes when a public,
non-final field is actually present for the property. For properties that have no
corresponding field (e.g., method-only properties, properties inherited from a superclass
without a re-declared field), deserialization should fall back to the normal setter path
rather than failing.

The fix must ensure the reader generation code never dereferences the Field reference
without a protective null-check, and only attempts field access when both the FieldBased
feature is active and a valid Field instance is available.
```

bug fix for @JSONType(deserializeFeatures=JSONReader.Feature.FieldBased), for issue #1054


Improve performance of `writeStringLatin1` in the vectorized writers.

In `JSONWriterUTF8Vector`, when `BrowserSecure` mode is active and the string uses Latin‑1 (`coder == 0`), delegate immediately to the existing `writeStringLatin1BrowserSecure` method (provided by the parent class) instead of performing browser‑secure character checks inside the vectorized loop and scalar fallback. Remove those checks from the fast path so it only handles the standard escaping needed for quotes, backslashes, and control characters.

In `JSONWriterUTF16Vector`, `writeStringLatin1` contains a redundant duplicate loop after the vectorized scan – eliminate it. The single remaining scalar loop should be sufficient after the vectorized scanning.

Update the benchmark comment in `UsersWriteUTF8BytesTest` with the new measured time for the vectorized UTF‑8 path.


Title: AutoType filter does not accept allowed types when deserializing JSONB with short type names

Context: `ContextAutoTypeBeforeHandler` (used via `JSONReader.autoTypeFilter`) is the standard way to whitelist types for JSONB deserialization when `WriteClassName` is active. The filter internally hashes accepted type names and checks against the type name found in the JSONB stream.

Problem:
- When JSONB encodes a type like `HashSet`, `Object[]`, or `List`, it may use abbreviated names (e.g., `"Set"`, `"HashSet"`, `"[O"`, `"O"` for `Object`) rather than the fully-qualified class name. Even if the user explicitly adds `HashSet.class` or `"Set"` as an accepted type, the filter rejects the deserialized type because the stream name does not match the expected hash, resulting in an auto-type-not-supported error.
- The same failure occurs for basic Java types (`Object`, `String`, primitives, core collections) that are often necessary for typical payloads. Currently there is no built-in way to safely allow these common, low-risk types without manual exhaustive listing.
- When the filter does not outright reject a type but falls through to `TypeUtils.loadClass`, several common short names (e.g., `"Set"`, `"HashSet"`, `"List"`, `"Map"`, `"[O"` for `Object[]`) are not recognized, causing `ClassNotFoundException` or incorrect type resolution.

Desired behavior / invariants:
1. The auto-type filter should correctly accept types that have been explicitly whitelisted, regardless of whether the JSONB stream uses the full class name or a standard abbreviated form used by the library.
2. Provide a straightforward mechanism to automatically include a default set of basic, safe types (`Object`, `String`, primitive wrappers, common collection interfaces and their standard implementations, etc.) in the whitelist, without the user enumerating each one.
3. `TypeUtils.loadClass` should be extended to resolve short class names that are already used by the library for serialization (e.g., `"Set"`, `"HashSet"`, `"List"`, `"Map"`, `"O"`/`"Object"`, `"[O"` for `Object[]`), so that if the filter permits a type, the class can actually be loaded.

Additional notes:
- The fix must not weaken security: the default basic set should only include types that are universally safe and necessary for typical data interchange.
- The change must not break existing whitelists that rely on full class names.
- Test scenarios: serializing a `HashSet` or an `Object[]` with `WriteClassName`, then deserializing with `autoTypeFilter` allowing the respective type, should succeed. Similarly, a filter created with the new “include basic types” option should allow `Object`, `String`, `HashMap`, etc. without additional configuration.


Title: Fix enum serialization with WriteEnumsUsingName producing corrupted UTF-8 bytes for non-ASCII names

Description:
When serializing an object to a byte array using `JSON.toJSONBytes(obj, JSONWriter.Feature.WriteEnumsUsingName)`, enum values that contain non-ASCII characters (such as Chinese, Japanese, or emoji) are encoded incorrectly. The resulting byte array, when interpreted as UTF-8, contains malformed JSON that cannot be parsed back into the original object. This roundtrip failure happens because the code that builds the byte[] for the enum name appears to assume single-byte characters, corrupting multi-byte UTF-8 sequences.

Steps to reproduce:
1. Define an enum with a non-ASCII name (e.g., `TestEnum { 第一, 第二 }`).
2. Create a bean containing that enum field, and serialize with `JSON.toJSONBytes(bean, WriteEnumsUsingName)`.
3. Convert the byte array to a string via `new String(bytes, StandardCharsets.UTF_8)` and attempt to parse it back with `JSONObject.parseObject`.
4. Observe that the enum field value is not preserved (parsing fails or returns an unexpected value).

Expected behavior:
The byte array should contain valid UTF-8 JSON with the enum names correctly encoded, so that a roundtrip (bytes → string → object) reproduces the original object faithfully.

Please fix the UTF-8 encoding of enum names in the `WriteEnumsUsingName` path so that non-ASCII characters are properly encoded as multi-byte sequences.


**Title:** Custom `ObjectWriter` registrations for `Long`, `Integer`, `Boolean`, `BigDecimal`, and `Date` are bypassed inside containers and fields

**Description:**
When a user registers a custom `ObjectWriter` for a specific type (e.g. `Long`, `Integer`, `Boolean`, `BigDecimal`, `Date`) via `JSON.register(type, writer)`, the custom serialization is **not** applied when values of that type appear inside a `Map`, a `List`, or as a bean field. The default serialization path is taken instead, producing output that ignores the custom writer.

For example, after registering a custom writer for `Long.class` that outputs the value as a string, serializing a `Map<String, Long>` or a `List<Long>` still renders the longs as plain numbers. The same problem occurs for custom writers registered for `Integer`, `Boolean`, `BigDecimal`, and `Date` in those contexts, and for `Date` fields of a JavaBean.

**Expected behavior:**
Custom `ObjectWriter`s registered through the provider must be used consistently across the serialization process – including inside `Map`s, `List`s, and for bean fields – regardless of any internal fast-path optimisations.

**Acceptance criteria:**
1. After `JSON.register(Long.class, customWriter)`, serializing a `Map<String, Long>` must invoke `customWriter` for each `Long` value.
2. After `JSON.register(Long.class, customWriter)`, serializing a `List<Long>` must invoke `customWriter` for each element.
3. Registered writers for `Integer`, `Boolean`, `BigDecimal`, and `Date` must be invoked in the same container contexts (Maps and Lists).
4. For a JavaBean field of type `Date`, a registered custom `Date` writer must be used instead of any default date-format logic.
5. The `WriteLongAsString` writer feature must still be honoured for `List`s (it already works for `Map`s).
6. The default (no-custom-writer) path must not regress in performance.

**Context:** This was raised as issue #866.

Long Type support user-define serialize, for issue #866


**Title:** Overloaded setter with `@JSONField` annotation not preferred when JSON value is an empty object

**Description:**
When a Java bean has several overloaded setter methods for the same property name (e.g., `setA(A1)`, `setA(A2)`, etc.) and the input JSON value for that property is an empty object `{}`, fastjson2 may choose an arbitrary setter instead of the one annotated with `@JSONField`.

In Fastjson 1 this annotation was used to give an overloaded setter higher priority when no type‑hierarchy clues (e.g., subclass relationships) could disambiguate the choice. After migrating to Fastjson 2, the annotated setter is no longer automatically preferred, leading to unexpected deserialization results.

**Expected behaviour:**
If exactly one of the applicable setter methods for a property has the `@JSONField` annotation, that setter should be selected as the target for deserialization. This rule should apply when the JSON value is an empty object or in any situation where multiple overloaded setters would otherwise be equally valid candidates and no stronger type‑based ordering (e.g., sub‑type/super‑type) dictates a specific choice.

**Repro outline (conceptual):**
- Define a bean with several `setA(...)` overloads taking unrelated parameter types `A1`–`A5`.
- Annotate only the `setA(A2)` overload with `@JSONField`.
- Deserialize `{"a":{}}` – the empty object could be deserialized as any of `A1`–`A5`.
- Currently the chosen setter is not necessarily the annotated one; the fix should make `A2` (the annotated parameter type) the selected setter.

support setter order with annotation @JSONField, for issue #1506


When a child class overrides a parent's public field with a getter/setter pair for the same property name, `JSON.toJSONString` and `JSON.parseObject` should respect the child's accessor methods rather than the parent's field. Currently, the parent field can "win", causing the serialized output and deserialized object to ignore values set through the child's getter/setter. This leads to surprising behavior where a subclass that encapsulates a field differently than its parent does not round-trip correctly.

Acceptance criteria:

- Serialization: if an object has both a parent-level public field and a child-level getter for the same logical JSON property, the getter's value must be used.
- Deserialization: when populating an object from JSON, the child's setter must be invoked if it exists, not the parent's field.
- This priority should hold regardless of whether the parent field and child accessors are both discovered during metadata scanning, and should not require manual annotations or order tweaks.
- The fix must work symmetrically for reading and writing paths (i.e., both `JSON.toJSONString` and `JSON.parseObject`).

bug fix for getter & setter override parent field, for issue #648


```
Title: List fields with null elements cause deserialization failures

When deserializing a JSON object that contains a List field with null entries (e.g., `{"items":[null, null]}`) into a typed Java bean, parsing fails. The expected behavior is that the deserialized List should preserve the null elements as `null` entries.

This affects:
- Public field Lists
- Private field Lists with getter/setter
- Private field Lists with only getter (initialized list)
- Field-based parsing (JSONReader.Feature.FieldBased)
- Various element types: custom bean types, String, Integer

The current behavior throws an exception when it encounters `null` where an object element is expected, instead of correctly setting the list element to null. This happens only in the JSON (non-JSONB) path and appears specific to ASM-generated object readers.

The fix should ensure that when the JSON token at an element position is `null`, the corresponding list entry is stored as null without attempting to instantiate an object. The fix should cover all the bean/list configurations listed above.
```

bug fix for deserialize List, for issue #573


```
Title: JSON serialization produces invalid raw control characters instead of \uXXXX escapes

Description:
When I serialize a Java String that contains invisible control characters (such as Unicode code points 0x00 through 0x1F that are not covered by the usual shorthand escapes like \n, \t, \r, \f, \b), the output JSON embeds those characters literally. For example, the character U+0011 (Device Control 1) appears as a raw byte/char in the JSON string, making the output invalid JSON. Downstream parsers then fail to parse the generated string.

Given:
String s = "{\"test\": \"Pixel \u0011 XL\"}";

Calling JSON.toJSONString(s) (or JSON.toJSONBytes) currently produces output that contains the raw control character instead of an escaped form like \u0011. The expected behavior is to escape all control characters in the range U+0000–U+001F (except for the characters that already have the standard two-character escapes: \b, \f, \n, \r, \t) as \uXXXX sequences, as required by the JSON specification.

This escaping should apply consistently across all output paths (string-based and byte-based) so that serialized JSON is always valid and can be round-tripped. Additionally, all four-character lowercase hex escapes should be used (e.g., \u0011, not \u011 or other short forms).

The bug was observed in fastjson2 2.0.6 (and likely earlier). The impact is that any string inadvertently containing these characters cannot be safely serialized and later deserialized.

Expected behavior:
- For the six control characters with well-known escapes (\b, \f, \n, \r, \t), the existing behavior is correct and should be preserved.
- For all other characters in the range 0x00–0x1F, the writer must emit a six-character \uXXXX escape using lowercase hex digits, e.g., \u0000 through \u001F (with gaps at the already-handled values).
- The fix should cover both the UTF-16 and UTF-8 writer implementations (including the JDK8 and JDK9-specific variants) so that JSON.toJSONString and JSON.toJSONBytes both produce correctly escaped output.
- Round-tripping should work: for any single control character, serializing and then parsing back yields the original string.
```

bug fix for write invisible characters, for issue #435


When parsing JSON numbers with many decimal places (e.g. `12345.6789`) into `BigDecimal`, `Float`, or `Double`, the deserialization is noticeably slower than expected. It appears the fast path that divides the unscaled integer value by a precomputed power of ten (`SMALL_10_POW`) in `JSONReader.getNumber()` only covers scales up to 10, while the internal scale byte can represent values up to 15. Numbers with 11–15 decimal digits fall through to a slower code path, hurting performance.

Extend the fast-path lookup table and the corresponding switch cases so that all possible scale values (0 through 15) are handled via the precomputed `SMALL_10_POW` array. Ensure that numbers like `12345.6789` (which have scale 4) are still handled correctly, and that scales 11–15 now take the optimized path instead of the fallback.

improved BigDecimal & Float & Double deserialize performance #602


We should use the fast 19-char millis reader for `@JSONField(format = "yyyy-MM-dd HH:mm:ss")` instead of going through the general date-parsing path. The format is common enough to justify a hardcoded fast path. The existing `JSONReader` already has a `readMillis19` method that handles exactly 19-character date strings like `"2022-09-07 12:38:31"` and `"2022-09-07T12:38:31"`. The fix should recognize this format string inside the field reader and dispatch directly to that method. All other date formats and parsing behaviour must stay unchanged.

improved performance JSONField#format 'yyyy-MM-dd HH:mm:ss' deserialize


json
{
  "message": "**Title:** Support `EnumSet` serialization and deserialization for JSON and JSONB\n\n**Background**\n`EnumSet` is a special-case `Set` that is not currently handled correctly during deserialization. While serializing an `EnumSet` to a JSON array of enum constant names works, the reverse operation fails—the deserialized object is a `HashSet` or another concrete set implementation rather than an `EnumSet` instance. This affects both the JSON text format and the JSONB binary format.\n\n**Acceptance Criteria**\n\n1. **Top-level `EnumSet` round-trip via JSON**  \n   - When an `EnumSet<TimeUnit>` (or any other enum type) is serialized using `JSON.toJSONString`, the result is a JSON array of the enum constant names (e.g., `[\"SECONDS\",\"DAYS\"]`).  \n   - Deserializing that JSON string back using `JSON.parseObject(text, new TypeReference<EnumSet<TimeUnit>>() {})` must produce an `EnumSet` containing the same enum constants.\n\n2. **Top-level `EnumSet` round-trip via JSONB**  \n   - Serializing an `EnumSet` with `JSONB.toBytes` and then deserializing with `JSONB.parseObject(bytes, new TypeReference<EnumSet<TimeUnit>>() {})` must reconstruct an equivalent `EnumSet`.\n\n3. **Bean fields of type `EnumSet`**  \n   - A Java bean with a field of type `EnumSet<TimeUnit>` must serialize that field as a JSON/JSONB array of enum constant names.\n   - Deserializing the bean’s JSON/JSONB representation must fill the field with an `EnumSet` instance, not a `HashSet` or any other collection type.\n\n4. **Generic type handling**  \n   - The fix must work for any enum type, not just `TimeUnit`, as long as the target type is expressed via `EnumSet<SomeEnum>` in the type information (e.g., through `TypeReference`, bean field generic type, etc.).\n\n**Additional Notes**\n- The existing `JSON.toJSONString` already correctly serializes an `EnumSet` as an array of enum names; ensure no regression there.\n- The `JSONB` binary serialization must also emit a format that can be decoded back into an `EnumSet` (currently it likely encodes it as a generic collection but fails on deserialization).\n- All changes must be compatible with the current public API surface; no new public classes or methods are required beyond what is needed to meet the criteria.",
  "supervision_value": 3,
  "difficulty": 2
}
```


When serializing a `Map` with `JSONWriter.Feature.ReferenceDetection` enabled, if a `PropertyPreFilter` calls `writer.getPath()`, the returned path does not include the map key for entries inside the map. For a bean field `map1` containing keys like `"childItem"`, the expected path during filtering is `"$.map1.childItem"` but the key portion is missing or wrong.

Fix so that `JSONWriter.setPath()` is called for each map entry *before* the pre‑filter runs, making `writer.getPath()` return the correct full path. The path state must be cleaned up via `popPath` after the value has been written, including when the value is null and skipped, when a filter rejects the entry, or when reference detection triggers a reference‑write instead of the full value.

fix JSONWriter#getPath not correct on Map, for issue #2590


### Bug: `WriteLongAsString` not applied to small long values during serialization

**Summary**
When the `WriteLongAsString` feature is enabled, certain small long field values (e.g., values in the range used by internal numeric caches) are still emitted as unquoted numbers rather than as strings. The feature should be honored for all long values, regardless of size.

**Steps to reproduce**
1. Define a simple Java object with a `long` field (e.g., `id`).
2. Set `id` to a small value like `1038` or `1039`.
3. Serialize the object using `JSON.toJSONString(obj, JSONWriter.Feature.WriteLongAsString)`.
4. Observe that the output is `{"id":1039}` instead of `{"id":"1039"}`.

**Expected behavior**
All `long` values must be written as JSON strings when `WriteLongAsString` is enabled, irrespective of the value magnitude and regardless of whether the writer uses an internal fast path for small numeric values.

**Additional considerations**
- The feature can be specified per-writer (globally) or per-field via `FieldWriter` configuration. Both sources must be respected when deciding whether to emit the value as a string.
- This applies to both UTF-8 and UTF-16 output paths.
- The fix should not break the existing fast path when neither `WriteLongAsString` nor `WriteNonStringValueAsString` is set.

bug fix for WriteLongAsString, for issue #961


**Title:** fastjson 1.x compatible JSONPath read/extract results do not use 1.x container types

**Description:**
The `com.alibaba.fastjson.JSONPath` static methods (`read` and `extract`) in the fastjson v1 compatibility module should return objects that match the fastjson 1.x API contract. In particular, when a JSONPath expression evaluates to an object or an array of objects, the result should contain `com.alibaba.fastjson.JSONObject` and `com.alibaba.fastjson.JSONArray` instances, not fastjson2 internal types.

Currently, some overloads of `JSONPath.read` and `JSONPath.extract` can produce fastjson2 object representations, which breaks downstream code that casts results to `JSONObject` or tries to use 1.x methods like `jsonObject.get("key")`. This inconsistency harms the drop-in compatibility the compat module is meant to provide.

**Expected behavior:**
All static `JSONPath.read(String json, String path)`, `read(String json, String path, Type clazz, ParserConfig parserConfig)`, and `extract(String json, String path)` methods must apply the same parser features used by the rest of the v1 compatibility layer (e.g., `JSON.parseObject`) to ensure that the resulting object tree consists of 1.x layer types.

**Non-obvious edge cases / safety constraints:**
- The fix must not change the behavior when the path targets scalar values; only container types (maps/arrays) are affected.
- The fix should reuse the existing parser context machinery already used in the compat module to create 1.x objects, ensuring uniform behavior.

improved fastjson 1.x jsonpath compatible, for issue #2476


# Feature Request: Multi-typed JSONPath should coerce results to the declared types

The `JSONPath.of(String[] paths, Type[] types)` API is intended to return an `Object[]` array where each element matches the corresponding declared `Type`. However, the implementation currently does not reliably convert the raw JSONPath results into the declared types. This forces callers to manually cast/convert each element, defeating the purpose of the typed multi-path API.

## Desired Behavior

When using a typed multi-path `JSONPath` (created via `JSONPath.of(paths, types)`), both `eval(Object rootObject)` and `extract(String jsonStr)` should produce an `Object[]` where each element is coerced to the exact declared type, following these rules:

1. **`String.class` target**: If the matched value is a JSON object or array (represented as a `Map`, `Collection`, or `JSONArray`), it should be serialized back to its JSON string representation. For example, a path returning an array `[{"name":"a.b"},{"name":"c"}]` should produce the string `"[{\"name\":\"a.b\"},{\"name\":\"c\"}]"`.

2. **`String[].class` target**: If the matched value is a JSON array of objects (represented as a `Collection` or object array), it should be converted to a `String[]` array. Each element must be the string representation of the corresponding array item (e.g., `"{\"name\":\"a.b\"}"` for an object item). Null/string items should be preserved as-is.

3. **`Long.class` target**: Numeric values should be converted to `Long` using a lossless, safe coercion (e.g., `TypeUtils.toLong`).

4. **`BigDecimal.class` target**: Numeric values should be converted to `BigDecimal` similarly.

5. Other target types should follow the existing `TypeUtils.cast` semantics as a fallback.

These conversions must be applied consistently regardless of whether the input is a parsed `Map` (common for `eval`) or a raw object tree (from `extract` via streaming JSON reading). The current code takes different paths for `Map` and non-`Map` root objects, and the conversions should be unified so that callers always get properly typed results.

## Example

```java
String json = "{\"v\": [{\"name\": \"a.b\"}, {\"name\": \"c\"}]}";
JSONPath path = JSONPath.of(
    new String[]{"$.v", "$.v[*]"},
    new Type[]{String.class, String[].class}
);
Object[] result = (Object[]) path.extract(json);
// result[0] should be String: "[{\"name\":\"a.b\"},{\"name\":\"c\"}]"
// result[1] should be String[]: ["{\"name\":\"a.b\"}", "{\"name\":\"c\"}"]
```

The same behavior should apply for `eval` with a pre-parsed `JSONObject`.

## Acceptance Criteria

- Multi-typed JSONPath with `String.class` target returns JSON string representations for matched objects/arrays.
- Multi-typed JSONPath with `String[].class` target returns a `String[]` array with stringified elements.
- Numeric target types `Long.class` and `BigDecimal.class` are correctly coerced.
- The behavior is consistent between `eval` and `extract` for both map-based and non-map-based root objects.


Please fix deserialization of Spring's `org.springframework.util.LinkedMultiValueMap` when used as a generic field type (e.g. `LinkedMultiValueMap<String, SomePojo>`). In JSON the field is represented as an object where each key maps to a JSON array of value objects. The expected result is a `LinkedMultiValueMap` instance whose keys map to `List<SomePojo>` containing the deserialized elements from the respective array. Currently this scenario either fails or produces an incorrect map structure. The fix must be specific to `LinkedMultiValueMap` and must not alter deserialization behavior for other map types.

fix deserialize generic LinkedMultiValueMap error, for issue #2489


```
The JSONPath filter syntax `[?(@.key)]` in fastjson2 currently includes objects where the field `key` exists but has a `null` value. This contradicts the semantics of the existence filter, which should only match objects where the named field is actually present with a non-null value. The same expression in fastjson v1 correctly omits null-valued entries, so this is a regression (issue #3825).

Change the evaluation of `[?(@.key)]` so that it returns only those objects for which the field value is not null. The filter should still exclude objects that do not have the field at all.
```

fix JSONPath filter expression ?(@.key) to exclude null values, for issue #3825


We observed that `JSON.toJSONString()` is significantly slower when serializing many `java.util.UUID` values compared to typical JSON objects. Profiling points to the write path that produces the quoted hex-string representation (36-character UUID string). The overhead is especially noticeable in services that handle large event batches where every record carries one or more UUIDs.

The byte-oriented (UTF‑8) writer already uses a fast algorithm that packs hex digits and writes multiple bytes with `UNSAFE`. The char‑oriented (UTF‑16) writer still seems to write each character individually via a per-character lookup table, causing many small writes and computation steps.

We need the UTF‑16 UUID serialization to be optimized in a similar fashion so that throughput is consistent regardless of the output encoding. The result must remain correct for all valid UUIDs (including those whose hex digits contain letters `a`–`f`) and must handle the existing writer contract (capacity checks, proper quoting, and safe memory access).

Please bring the UTF‑16 UUID serialization path up to parity with the optimized byte path, ensuring it uses bulk writes where possible and minimizes per-character overhead.


**Title:** Extended `Map` subclasses with extra fields should serialize as plain maps

**Description:**
When a `Map` subclass declares its own additional fields, the current serialization path treats the object as a JavaBean and includes the extra fields in the output. For ordinary `Map` implementations only the entry data is written, and the same behavior should apply to these “extended” map subclasses—the extra fields are implementation details that users do not expect to see in the JSON.

This works correctly for subclasses of `HashMap` and `LinkedHashMap`, but fails for subclasses of `TreeMap` (and likely other `Map` types not covered by existing checks). The expected outcome:

- Serialize only the map entries, ignoring any extra fields the subclass may declare.

**Acceptance criteria / observable behavior:**
- Given a custom `TreeMap` subclass that has an additional `String name` field and contains map entries, calling `JSON.toJSONString(map)` must produce only the entry-based JSON (e.g., `{"key":value}`) and must not include the extra field.
- The fix should apply to all `Map` subclasses that have extra fields, not just `TreeMap`.
- Existing behavior for standard `Map` implementations and for `HashMap`/`LinkedHashMap` subclasses must remain unchanged.

**Note:** There is a known utility (`BeanUtils.isExtendedMap`) that detects Map subclasses with extra fields; the object-writer provider should use this (or equivalent existing infrastructure) to route such objects to the map writer instead of the bean-based writer.

fix : extended Map support, for issue #1338


When serializing `BigDecimal` values where the unscaled value has fewer digits than the scale, the JSON output omits leading zeros in the decimal fraction. For example:

- `new BigDecimal("1.096")` serializes as `"1.96"` instead of `"1.096"`.
- `new BigDecimal("1.06")` serializes as `"1.6"`.
- `new BigDecimal("1.09600000")` serializes as `"1.96"` instead of preserving the trailing zeros.

The problem occurs for both `JSON.toJSONString(decimal)` and `JSON.toJSONBytes(decimal)`.

The expected behavior is that the serialized JSON string for a `BigDecimal` directly corresponds to the canonical string representation of the `BigDecimal`, preserving any leading or trailing zeros in the fractional part implied by its scale. The output must reflect the exact precision and scale of the original value.

fix serialize decimal result incorrect, for issue #1831


```text
Title: fastjson2 fails at runtime under GraalVM native image due to reflective access to java.lang.String internals

Description:
When fastjson2 is compiled into a GraalVM native image, any code path that attempts to construct a String from a UTF‑16 byte array (and possibly other String‑internal inspection) throws an error because the library calls `Field.setAccessible(true)` on private fields of `java.lang.String`. GraalVM’s closed‑world analysis prohibits reflective access to these fields, causing a fatal error at runtime. This blocks all JSON parsing in native images.

The library already has a `UnsafeUtils` class that uses `sun.misc.Unsafe` for low‑level operations; it already possesses a `UTF16StringCreator` inner class that currently depends on `setAccessible(true)` before obtaining field offsets via `Unsafe.objectFieldOffset`. The fix should eliminate the `setAccessible(true)` calls, relying solely on `Unsafe` to read the “coder” byte and the backing byte array from String objects.

Additionally, we need a way to determine whether a String uses ASCII/LATIN1 or UTF‑16 encoding (the “coder” byte) and to retrieve the backing byte array, both without triggering access restrictions. These capabilities must correctly handle Java 8 (where the “coder” concept does not exist) as well as Java 9+.

Acceptance criteria:
- Under GraalVM native image (or any environment where `setAccessible` is disallowed), creating a String from a UTF‑16 byte array must succeed without exceptions.
- The existing functionality on standard HotSpot JVMs (JDK 8, 11, 17, …) must remain unchanged.
- The implementation should avoid any call to `Field.setAccessible(true)` on `java.lang.String` fields. If field-offset caching is needed, it should be done via `Unsafe.objectFieldOffset` which does not require `setAccessible`.
- Reading the “coder” byte and the backing value (as `byte[]`) from a String instance should be reasonably efficient and safe, with proper null checks.
- The solution should be confined to the utility layer (`UnsafeUtils`) so that other parts of the code can use it when they need to introspect String internals.
```


Fix `SeeAlso` deserialization in JSONB when the type discriminator is not the first field.

When an interface/class uses `@JSONType(seeAlso = ..., typeKey = "type")` and the data is serialized/deserialized via JSONB, the deserialization into the correct subtype currently only succeeds if the type field appears first in the JSONB object. If any field precedes the type key, the deserialization incorrectly returns the base type or fails. This works correctly for text JSON, so the JSONB path should be aligned to respect the type key regardless of field order.

fix SeeAlso deserialize not work on non-first-type, for issue #3440


**Title:** `InstantiationException` thrown on JSONB deserialization when a `Supplier`-based `creator` succeeds and `hasDefaultValue` is true

**Component:** `core` – `ObjectReaderAdapter`
**Severity:** medium – valid deserialization path fails with an unexpected exception

### Problem
When an `ObjectReaderAdapter` creates a target object using its `Supplier<T> creator` (non-constructor path) and the object has default values that need to be initialized (`hasDefaultValue == true`), the method `createInstance` successfully allocates the object and calls `initDefaultValue` but does **not** return the object. Execution continues past the block and falls into the error-handling tail of the method, which wraps any previous instantiation error in an `InstantiationException` (or constructs a fresh one if the error reference is `null`). The result is an `InstantiationException` thrown from `createInstance` even though the instance was created without any actual instantiation failure.

This manifests concretely during JSONB deserialization of beans that contain `transient` fields (e.g., `transient AtomicBoolean`), where the auto-type/reflection machinery routes through the `Supplier` path and `hasDefaultValue` is true. Instead of getting the deserialized object back, users see:

```
com.alibaba.fastjson2.JSONException: InstantiationException ...
```

### Expected behavior
When the `Supplier` successfully provides an object and the default-value initialization completes without error, `createInstance` should return that object immediately. No `InstantiationException` should be raised for this code path.

### Additional notes
- The bug is in `ObjectReaderAdapter.createInstance(long features)` – specifically, a missing `return object;` after the `if (hasDefaultValue) { initDefaultValue(object); }` block.
- The patch should only add the single `return object;` statement inside the `if (creator instanceof Supplier)` branch, just after the `if (hasDefaultValue)` block, so that the correct instance is returned.
- Related test cleanup (removing unnecessary `throws Exception` from `TransientTest.test_for_transient` and whitespace adjustments) can be done together, but the core fix is the missing return.


**Request: Support `randomIndex()` in JSONPath streaming extraction (`extract(JSONReader)`)**

Currently, the `randomIndex()` array accessor in JSONPath works correctly when evaluating an already-parsed object tree via `eval`, e.g.:

```java
JSONPath.of("$.data[randomIndex()]").eval(jsonObject);
```

However, using the exact same path expression with the streaming extraction API (`extract(JSONReader)`) throws an exception:

```java
JSONPath path = JSONPath.of("$.data[randomIndex()]");
JSONReader reader = JSONReader.of("{\"data\": [...]}");
Object value = path.extract(reader); // throws
```

This applies to any expression containing `randomIndex()`, including nested ones like `$.data[randomIndex()].name`, and affects both standard text JSON readers and `JSONB` binary readers.

**Desired behavior:**

- `JSONPath.extract(JSONReader)` should support `randomIndex()` and, like `eval`, randomly select one element from the target array.
- The selection must happen during streaming parsing (i.e., without loading the entire array into an intermediate tree, unless unavoidable for random access).
- The behavior must be consistent across JSON text (`JSONReader.of(String)`, `JSONReader.of(byte[])`, etc.) and `JSONB` binary (`JSONReader.ofJSONB(byte[])`) inputs.
- Existing `eval` behavior for `randomIndex()` must remain unchanged.

**Constraints:**
- The path parsing step (`JSONPath.of(...)`) must correctly recognize `randomIndex()` without causing a parse error or off-by-one consumption of characters in the path string.
- The implementation should re‑use the same randomization logic already present in `eval` and should not introduce new external randomness requirements or API surface.
- The fix should not alter the behavior of any other JSONPath function or segment.

fix randomIndex throw exception when use JSONPath.of(JSONReader)


```
`@JsonPropertyOrder` from Jackson is not respected during serialization.
When a class is annotated with `@JsonPropertyOrder({"a","b"})` the resulting JSON should emit properties in that order
(`a` then `b`), overriding any default ordering. If the annotation is present but provides an empty `value`, no
reordering should be applied. This should integrate with the existing Jackson-annotation compatibility support and
reuse the same ordering pathway that `@JSONType(orders=...)` already uses.
```

support jackson annotation JsonPropertyOrder


When converting a `JSONArray` that contains nested JSON arrays or raw Java arrays to a typed multidimensional array (e.g. `int[][]`, `Integer[][]`, etc.) via methods like `JSONArray.to(Class)`, the library either throws an error or produces incorrect results. This affects use cases where the outer array is built dynamically and may contain heterogeneous inner arrays (JSON arrays with `null`, the string `"null"`, empty strings, or actual `String[]`, `int[]`, etc.).

**Expected behavior:**
`JSONArray.to(SomeArrayType.class)` should traverse and convert nested array elements correctly, similar to how it already handles `Map` and `Collection` elements. Specifically:

- When an element is a `null` reference, the string `"null"`, or an empty string in a position that maps to a numeric type, it should become `0` for primitive component types and `null` for boxed component types.
- When an element is a raw Java array (e.g. `String[]`, `int[]`, `boolean[]`, `float[]`, etc.), it should be treated as if it were a `JSONArray` containing those values, and converted to the appropriate target component type.
- Numeric conversion between primitive types should be supported (e.g. a `byte[]` element should convert to an `int[]` row when casting to `int[][]`).

**Impact:**
Users building complex data structures from mixed sources (JSON parsed from external systems, programmatic `JSONArray` construction, or native arrays) cannot reliably cast them to typed multidimensional arrays. This breaks ergonomic `to(List)Object`/`toJavaList` usage and forces manual pre‑flattening and error‑prone boilerplate.


```
Title: JSONPath: missing ceil/ceiling functions, and size() returns wrong value for scalars

JSONPath expressions using `ceil()` or `ceiling()` are not recognized and throw an error,
while `floor()` works correctly.  We need `ceil`/`ceiling` support with the same behaviour as `floor`:
apply `Math.ceil` / `RoundingMode.CEILING` to numbers (Double, Float, BigDecimal), and when the
input is a List, apply the operation to each numeric element in-place.

Additionally, applying `$.size()` on a scalar value (e.g. a plain number) returns `-1`.
According to the JSONPath semantics we target (Trino-style), `size()` should return `1` for any
non-collection value – a scalar counts as a single element.  The current `-1` breaks queries that
expect `size()` to always produce a non-negative integer.

Steps to reproduce:
- `JSONPath.eval( -2.0, "$.size()" )` → returns -1; expected 1.
- `JSONPath.extract("2.1", "$.ceil()")` → throws an unsupported-function error; expected `"3"`.
- `JSONPath.extract("-1.5", "$.ceiling()")` → same error; expected `"-1"`.

Expected behaviour:
- `ceil()` and `ceiling()` act as function segments, analogous to the existing `floor()`, and
  accept the same numeric and list-of-numeric inputs.
- `size()` returns `1` for any scalar input (not just arrays, collections, maps, and strings).
```


When a parent class declares a field with `@JSONField(serialize = false)` and a subclass hides that field (declares a new field with the same name) while also overriding the accessor methods, serializing an instance of the subclass incorrectly still drops the property. The parent annotation should not force the property to be ignored in the subclass; the subclass´s own declarations and accessors should be honoured unless the subclass itself opts out. Fix the serialization pipeline so that a `serialize = false` annotation on a parent class field does not propagate to a subclass that shadows the field and provides its own getter/setter.

fix ignore overwrite not work, for issue #1513