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

jamesbognar pushed a commit to branch docs
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/docs by this push:
     new 5ba88b1d5b Add per-session property overrides documentation for 9.2.1
5ba88b1d5b is described below

commit 5ba88b1d5b21702b6708e49633f742186baaa29e
Author: James Bognar <[email protected]>
AuthorDate: Sat Apr 4 13:52:42 2026 -0400

    Add per-session property overrides documentation for 9.2.1
    
    Documents the new Session.Builder.property()/properties() dispatch feature,
    including all newly exposed session properties across JSON, UON, URL 
encoding,
    XML, HTML, and CSV serializers/parsers, plus the context bypass bug fixes.
    
    Made-with: Cursor
---
 pages/release-notes/9.2.1.md                |  53 +++++++
 pages/topics/02.03.SerializersAndParsers.md | 222 +++++++++++++++++++++++++++-
 2 files changed, 274 insertions(+), 1 deletion(-)

diff --git a/pages/release-notes/9.2.1.md b/pages/release-notes/9.2.1.md
index 49ca15e259..9ae953978c 100644
--- a/pages/release-notes/9.2.1.md
+++ b/pages/release-notes/9.2.1.md
@@ -641,6 +641,59 @@ ClassInfoTyped<String> ci = ClassInfo.of(String.class);
 // ci carries type information for String
 ```
 
+#### Per-Session Property Overrides (Extended)
+
+`Session.Builder.property(String, Object)` and `properties(Map)` now dispatch 
to typed session-level setter methods across a broad range of serializer and 
parser session classes, enabling per-request override of serializer/parser 
configuration without rebuilding the context.
+
+Both short names (e.g. `"escapeSolidus"`) and fully-qualified names (e.g. 
`"JsonSerializerSession.escapeSolidus"`) are accepted as keys. `BasicConverter` 
handles type coercion so string values (e.g. `"true"`) are automatically 
converted to the expected target types.
+
+```java
+// Per-request JSON settings without rebuilding the serializer
+String json = JsonSerializer.DEFAULT
+    .createSession()
+    .property("escapeSolidus", true)
+    .property("addBeanTypes", true)
+    .build()
+    .serialize(myBean);
+
+// Per-request parser settings
+MyBean bean = JsonParser.DEFAULT
+    .createSession()
+    .property("trimStrings", true)
+    .property("validateEnd", true)
+    .build()
+    .parse(json, MyBean.class);
+```
+
+##### New Session-Level Properties
+
+| Session Class | New Property Keys |
+|---|---|
+| `ParserSession` | `trimStrings` |
+| `JsonSerializerSession` | `escapeSolidus` |
+| `JsonParserSession` | `validateEnd` |
+| `UonSerializerSession` | `encoding`, `paramFormat` |
+| `UonParserSession` | `validateEnd` |
+| `UrlEncodingSerializerSession` | `expandedParams` |
+| `UrlEncodingParserSession` | `expandedParams` |
+| `XmlSerializerSession` | `addNamespaceUrisToRoot`, `autoDetectNamespaces`, 
`defaultNamespace`, `enableNamespaces`, `textNodeDelimiter` |
+| `XmlParserSession` | `preserveRootElement`, `validating` |
+| `HtmlSerializerSession` | `addKeyValueTableHeaders`, 
`detectLabelParameters`, `detectLinksInStrings`, `labelParameter`, 
`uriAnchorText` |
+
+These complement the properties added in the initial release of this feature:
+- `BeanTraverseSession`: `initialDepth`
+- `SerializerSession`: `keepNullProperties`, `trimStrings`, `addBeanTypes`, 
`addRootType`, `sortCollections`, `sortMaps`, `trimEmptyCollections`, 
`trimEmptyMaps`
+- `WriterSerializerSession`: `maxIndent`, `quoteChar`
+- `CsvSerializerSession`: `byteArrayFormat`, `allowNestedStructures`, 
`nullValue`
+
+##### Bug Fixes — Context Bypass
+
+Several session classes were overriding accessor methods to return 
`ctx.isAddBeanTypes()` directly, bypassing the session-level field. This meant 
that per-session `addBeanTypes` overrides set via `property("addBeanTypes", 
true)` had no effect for those serializers. The affected classes now correctly 
delegate to `super.isAddBeanTypes()`:
+
+- `JsonSerializerSession`, `UonSerializerSession`, `XmlSerializerSession`, 
`HtmlSerializerSession`, `MsgPackSerializerSession`, `YamlSerializerSession`, 
`CborSerializerSession`, `BsonSerializerSession`, `RdfSerializerSession`, 
`RdfStreamSerializerSession`
+
+Similarly, `ParserSession.isTrimStrings()` now returns the session-level field 
(initialized from the context) instead of delegating to `ctx.isTrimStrings()`. 
And `UonSerializerSession.getQuoteChar()` no longer bypasses the 
`WriterSerializerSession` session field.
+
 ### juneau-marshall-rdf
 
 #### Upgraded Apache Jena to 5.6.0
diff --git a/pages/topics/02.03.SerializersAndParsers.md 
b/pages/topics/02.03.SerializersAndParsers.md
index e3a7f831d7..977797ecea 100644
--- a/pages/topics/02.03.SerializersAndParsers.md
+++ b/pages/topics/02.03.SerializersAndParsers.md
@@ -155,4 +155,224 @@ Many of the JSON examples provided will use JSON5 syntax 
which is easier to read
 <node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/examples/parser/ImageParser.html" 
target="_blank">ImageParser</a></java-class> - Example of a custom 
parser.</node-0>
 </tree>
 
-:::
\ No newline at end of file
+:::
+
+## Per-Session Property Overrides
+
+Serializer and parser contexts are immutable once built. However, individual 
_sessions_ (created via `createSession()`) support per-request configuration 
overrides without rebuilding the context.
+
+Use `Session.Builder.property(String key, Object value)` or 
`properties(Map<String,Object>)` to override specific settings for a single 
serialization or parse operation:
+
+```java
+// Override settings for a single request without rebuilding the serializer
+String json = JsonSerializer.DEFAULT
+    .createSession()
+    .property("escapeSolidus", true)
+    .property("addBeanTypes", true)
+    .property("maxIndent", 2)
+    .build()
+    .serialize(myBean);
+
+// Override parser settings per-request
+MyBean bean = JsonParser.DEFAULT
+    .createSession()
+    .property("trimStrings", true)
+    .property("validateEnd", true)
+    .build()
+    .parse(json, MyBean.class);
+```
+
+Both short property names (e.g. `"escapeSolidus"`) and fully-qualified names 
(e.g. `"JsonSerializerSession.escapeSolidus"`) are accepted. String values are 
automatically coerced to the target type using `BasicConverter`, so you can 
pass `"true"` where a `boolean` is expected, or `"fr-FR"` where a `Locale` is 
expected.
+
+```java
+// All of these are equivalent
+session.property("escapeSolidus", true);
+session.property("escapeSolidus", "true");
+session.property("JsonSerializerSession.escapeSolidus", true);
+```
+
+### Base Session Properties
+
+These properties are available on all sessions regardless of format:
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `locale` / `BeanSession.locale` | `Locale` | `BeanSession` | Locale used for 
date/number formatting |
+| `timeZone` / `BeanSession.timeZone` | `TimeZone` | `BeanSession` | Time zone 
used for date/calendar types |
+| `mediaType` / `BeanSession.mediaType` | `MediaType` | `BeanSession` | The 
media type being produced or consumed |
+| `debug` | `Boolean` | `ContextSession` | Enable debug mode |
+
+### Serializer Session Properties
+
+These properties apply to all serializers:
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `addBeanTypes` | `boolean` | `SerializerSession` | Add `_type` properties to 
beans when type cannot be inferred from reflection |
+| `addRootType` | `boolean` | `SerializerSession` | Add `_type` to the root 
object |
+| `keepNullProperties` | `boolean` | `SerializerSession` | Serialize `null` 
bean properties (default: `false`) |
+| `trimStrings` | `boolean` | `SerializerSession` | Trim whitespace from 
serialized strings |
+| `sortCollections` | `boolean` | `SerializerSession` | Sort collections 
before serializing |
+| `sortMaps` | `boolean` | `SerializerSession` | Sort maps by key before 
serializing |
+| `trimEmptyCollections` | `boolean` | `SerializerSession` | Omit empty 
collections from output |
+| `trimEmptyMaps` | `boolean` | `SerializerSession` | Omit empty maps from 
output |
+| `initialDepth` / `BeanTraverseSession.initialDepth` | `int` | 
`BeanTraverseSession` | Initial depth for indentation |
+| `maxIndent` / `WriterSerializerSession.maxIndent` | `int` | 
`WriterSerializerSession` | Maximum indentation depth for whitespace formatting 
|
+| `quoteChar` / `WriterSerializerSession.quoteChar` | `char` | 
`WriterSerializerSession` | Quote character for string values |
+| `uriContext` | `UriContext` | `SerializerSession` | Context for URI 
resolution |
+
+### Parser Session Properties
+
+These properties apply to all parsers:
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `trimStrings` / `ParserSession.trimStrings` | `boolean` | `ParserSession` | 
Trim whitespace from parsed string values |
+
+### Format-Specific Session Properties
+
+#### JSON / JSON5
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `escapeSolidus` / `JsonSerializerSession.escapeSolidus` | `boolean` | 
`JsonSerializerSession` | Prefix `/` characters with `\` escape |
+| `validateEnd` / `JsonParserSession.validateEnd` | `boolean` | 
`JsonParserSession` | Validate that no trailing content follows the parsed 
value |
+
+```java
+// Escape forward slashes in URLs
+String json = JsonSerializer.DEFAULT
+    .createSession()
+    .property("escapeSolidus", true)
+    .build()
+    .serialize("http://example.com";);
+// Produces: "http:\/\/example.com"
+
+// Require clean end-of-input
+JsonParser.DEFAULT
+    .createSession()
+    .property("validateEnd", true)
+    .build()
+    .parse("{\"a\":1}", JsonMap.class);  // ok
+```
+
+#### UON (URL-Encoded Object Notation)
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `encoding` / `UonSerializerSession.encoding` | `boolean` | 
`UonSerializerSession` | Percent-encode non-valid URI characters |
+| `paramFormat` / `UonSerializerSession.paramFormat` | `ParamFormat` | 
`UonSerializerSession` | Format for parameter values (`UON` or `PLAINTEXT`) |
+| `validateEnd` / `UonParserSession.validateEnd` | `boolean` | 
`UonParserSession` | Validate that no trailing content follows the parsed value 
|
+
+```java
+// Use plain-text parameter format (no UON escaping)
+String uon = UonSerializer.DEFAULT
+    .createSession()
+    .property("paramFormat", ParamFormat.PLAINTEXT)
+    .build()
+    .serialize("hello world");
+// Produces: hello world
+```
+
+#### URL Encoding
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `expandedParams` / `UrlEncodingSerializerSession.expandedParams` | `boolean` 
| `UrlEncodingSerializerSession` | Serialize array/collection bean properties 
as separate `key=value` pairs |
+| `expandedParams` / `UrlEncodingParserSession.expandedParams` | `boolean` | 
`UrlEncodingParserSession` | Parse repeated keys as array/collection values |
+
+```java
+// Serialize array as repeated parameters: a=1&a=2&a=3
+String urlencoded = UrlEncodingSerializer.DEFAULT
+    .createSession()
+    .property("expandedParams", true)
+    .build()
+    .serialize(JsonMap.of("a", new int[]{1, 2, 3}));
+```
+
+#### XML
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `enableNamespaces` / `XmlSerializerSession.enableNamespaces` | `boolean` | 
`XmlSerializerSession` | Include XML namespaces in output |
+| `addNamespaceUrisToRoot` / `XmlSerializerSession.addNamespaceUrisToRoot` | 
`boolean` | `XmlSerializerSession` | Add `xmlns:x` attributes to the root 
element |
+| `autoDetectNamespaces` / `XmlSerializerSession.autoDetectNamespaces` | 
`boolean` | `XmlSerializerSession` | Auto-detect namespace usage before 
serializing |
+| `defaultNamespace` / `XmlSerializerSession.defaultNamespace` | `String` | 
`XmlSerializerSession` | Default namespace (e.g. 
`"juneau:http://www.apache.org/juneau/"`) |
+| `textNodeDelimiter` / `XmlSerializerSession.textNodeDelimiter` | `String` | 
`XmlSerializerSession` | Delimiter for mixed-content text nodes |
+| `preserveRootElement` / `XmlParserSession.preserveRootElement` | `boolean` | 
`XmlParserSession` | Preserve the root element name when parsing into a generic 
`JsonMap` |
+| `validating` / `XmlParserSession.validating` | `boolean` | 
`XmlParserSession` | Validate XML against its schema during parsing |
+
+```java
+// Parse XML preserving the root element name
+JsonMap result = XmlParser.DEFAULT
+    .createSession()
+    .property("preserveRootElement", true)
+    .build()
+    .parse("<root><a>1</a></root>", JsonMap.class);
+// result = {"root": {"a": "1"}}
+```
+
+#### HTML
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `addKeyValueTableHeaders` / `HtmlSerializerSession.addKeyValueTableHeaders` 
| `boolean` | `HtmlSerializerSession` | Add `key` / `value` column headers to 
bean/map tables |
+| `detectLinksInStrings` / `HtmlSerializerSession.detectLinksInStrings` | 
`boolean` | `HtmlSerializerSession` | Auto-convert URL strings to `<a href>` 
anchor tags |
+| `detectLabelParameters` / `HtmlSerializerSession.detectLabelParameters` | 
`boolean` | `HtmlSerializerSession` | Look for label query parameters in URLs 
for anchor text |
+| `labelParameter` / `HtmlSerializerSession.labelParameter` | `String` | 
`HtmlSerializerSession` | Query parameter name to use as the anchor label 
(default: `"label"`) |
+| `uriAnchorText` / `HtmlSerializerSession.uriAnchorText` | `AnchorText` | 
`HtmlSerializerSession` | Strategy for generating anchor text from URIs |
+
+```java
+// Use a custom label parameter name
+String html = HtmlSerializer.DEFAULT
+    .createSession()
+    .property("labelParameter", "title")
+    .build()
+    .serialize("http://example.com?title=My+Page";);
+// Anchor text will be "My Page"
+
+// Disable auto-link detection
+String html2 = HtmlSerializer.DEFAULT
+    .createSession()
+    .property("detectLinksInStrings", false)
+    .build()
+    .serialize("http://example.com";);
+// Produces plain text, not an <a> tag
+```
+
+#### CSV
+
+| Property | Type | Session Class | Description |
+|---|---|---|---|
+| `byteArrayFormat` / `CsvSerializerSession.byteArrayFormat` | 
`ByteArrayFormat` | `CsvSerializerSession` | Format for `byte[]` values (e.g. 
`BASE64`, `HEX`, `SEMICOLON_DELIMITED`) |
+| `allowNestedStructures` / `CsvSerializerSession.allowNestedStructures` | 
`boolean` | `CsvSerializerSession` | Allow nested bean structures in CSV output 
|
+| `nullValue` / `CsvSerializerSession.nullValue` | `String` | 
`CsvSerializerSession` | String representation for `null` values (default: 
empty string) |
+
+```java
+// Use "N/A" for null values in CSV output
+String csv = CsvSerializer.DEFAULT
+    .createSession()
+    .property("nullValue", "N/A")
+    .build()
+    .serialize(JsonMap.of("name", null, "age", 30));
+```
+
+### REST Integration
+
+Per-session properties are particularly useful in REST contexts where 
per-request control is needed. Use `RestRequest` to set serializer and parser 
session properties for the current request:
+
+```java
+@RestGet
+public MyBean get(RestRequest req) {
+    // Configure serializer for this specific request
+    req.setSerializerSessionProperty("addBeanTypes", true);
+    req.setSerializerSessionProperty("maxIndent", 2);
+    return myBean;
+}
+
+@RestPut
+public void put(RestRequest req, @Body MyBean body) {
+    // Configure parser for this specific request
+    req.setParserSessionProperty("trimStrings", true);
+    // body is then parsed with trimStrings=true
+}
+```
\ No newline at end of file

Reply via email to