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

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 8e0ffc7f9b Add field filtering documentation to Spring Boot userguide
8e0ffc7f9b is described below

commit 8e0ffc7f9b6450d24711123a91f8aa74648a5997
Author: Robert Lazarski <[email protected]>
AuthorDate: Tue Apr 21 17:57:10 2026 -1000

    Add field filtering documentation to Spring Boot userguide
    
    Documents the ?fields= query parameter with configuration, usage
    examples (flat and multi-level dot-notation), a 97% payload reduction
    example, competitive comparison table (Spring, GraphQL, gRPC, Django,
    FastAPI), and Axis2/C parity notes.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 src/site/xdoc/docs/json-springboot-userguide.xml | 165 +----------------------
 src/site/xdoc/docs/json-streaming-formatter.xml  | 106 ++++++++++++---
 2 files changed, 94 insertions(+), 177 deletions(-)

diff --git a/src/site/xdoc/docs/json-springboot-userguide.xml 
b/src/site/xdoc/docs/json-springboot-userguide.xml
index 68302aeb4e..907e0d7006 100644
--- a/src/site/xdoc/docs/json-springboot-userguide.xml
+++ b/src/site/xdoc/docs/json-springboot-userguide.xml
@@ -511,167 +511,10 @@ curl -v -H "Authorization: Bearer 95104Rn2I2oEATfuI90N" \
 
 <h2>Response Field Filtering</h2>
 
-<p>Axis2/Java supports <code>?fields=</code> query parameter filtering that
-reduces response payload size by serializing only the requested fields.
-Filtering happens during streaming serialization — excluded fields are
-never buffered, never written to the wire. No service-side code changes
-are required.</p>
-
-<h3>Configuration</h3>
-
-<p>In <code>axis2.xml</code>, wrap the streaming formatter with
-<code>FieldFilteringMessageFormatter</code>:</p>
-
-<pre>
-&lt;messageFormatter contentType="application/json"
-    class="org.apache.axis2.json.streaming.FieldFilteringMessageFormatter"&gt;
-    &lt;parameter 
name="delegateFormatter"&gt;org.apache.axis2.json.streaming.MoshiStreamingMessageFormatter&lt;/parameter&gt;
-&lt;/messageFormatter&gt;
-</pre>
-
-<p>Without <code>?fields=</code> in the request, the formatter is a 
zero-overhead
-pass-through to the delegate.</p>
-
-<h3>Usage</h3>
-
-<p><strong>Flat filtering</strong> — select top-level fields:</p>
-<pre>
-GET /services/MyService?fields=status,result
-</pre>
-
-<p><strong>Dot-notation</strong> — filter inside nested objects and 
collections:</p>
-<pre>
-GET /services/MyService?fields=status,data.records.id,data.records.name
-</pre>
-
-<p>This walks three levels: keep <code>data</code> at top level, keep
-<code>records</code> inside it, keep only <code>id</code> and <code>name</code>
-inside each element of the <code>records</code> array. Multi-level dot-notation
-works on POJOs, Maps, and Collections — including
-<code>Map&lt;String, Object&gt;</code> containing
-<code>List&lt;Map&lt;String, Object&gt;&gt;</code>, which is the structure
-produced by JSON-RPC services that parse JSON into Java Collections.</p>
-
-<h3>Why Multi-Level Dots: A Java Collections Example</h3>
-
-<p>Consider a service that returns a response POJO where the heavy data
-is inside a <code>Map&lt;String, Object&gt;</code>. This is common when the
-service parses JSON from a backend (e.g., a calculation engine) into
-Java Collections rather than typed POJOs:</p>
-
-<pre>
-// The response POJO — what the Axis2 service method returns
-public class ServiceResponse {
-    private String status;
-    private long responseTimeMs;
-    private Map&lt;String, Object&gt; data;   // parsed from backend JSON
-    // getters, setters
-}
-</pre>
-
-<p>The <code>data</code> map contains multiple keys. One of them holds a
-list of records — each record is itself a map with 100+ fields:</p>
-
-<pre>
-// What "data" looks like at runtime (after JSON parsing):
-//
-// data = {
-//   "records" -> List&lt;Map&lt;String, Object&gt;&gt;   &lt;-- 100+ fields 
each
-//   "metadata" -> Map&lt;String, Object&gt;
-//   "diagnostics" -> Map&lt;String, Object&gt;
-// }
-//
-// Each record in the list:
-// { "id": "item-1", "name": "Widget A", "price": 19.99,
-//   "category": "HARDWARE", ... 96 more fields ... }
-</pre>
-
-<p>The Java path from the response root to a record field is three levels 
deep:</p>
-
-<pre>
-response.getData()                    // Map&lt;String, Object&gt;  = "data"
-    .get("records")                   // List&lt;Map&gt;            = 
"data.records"
-    .get(0).get("id")                 // Object               = 
"data.records.id"
-</pre>
-
-<p>That maps directly to the <code>?fields=</code> syntax:</p>
-
-<pre>
-?fields=status,data.records.id,data.records.name
-   |       |         |          |
-   |       |         |          +-- 3rd dot level: key inside each List element
-   |       |         +------------- 2nd dot level: key inside the data Map
-   |       +----------------------- 1st dot level: field on the response POJO
-   +------------------------------- top-level POJO field (no dots)
-</pre>
-
-<h3>Example: Before and After</h3>
-
-<p>A service returns a 5MB response with 127 fields per record:</p>
-<pre>
-{"response": {
-    "status": "SUCCESS",
-    "data": {
-        "records": [
-            {"id": "item-1", "name": "Widget A", ... 125 more fields ...},
-            {"id": "item-2", "name": "Widget B", ... 125 more fields ...}
-        ],
-        "metadata": {...},
-        "diagnostics": {...}
-    }
-}}
-</pre>
-
-<p>With <code>?fields=status,data.records.id</code>:</p>
-<pre>
-{"response": {
-    "status": "SUCCESS",
-    "data": {
-        "records": [
-            {"id": "item-1"},
-            {"id": "item-2"}
-        ]
-    }
-}}
-</pre>
-
-<p>The metadata, diagnostics, and 126 unused fields per record are never
-serialized. A 5MB response becomes ~150KB — a 97% reduction.</p>
-
-<h3>Competitive Context</h3>
-
-<p>No other Java or Python JSON web services framework ships recursive
-multi-level field filtering that operates on runtime Maps and Collections
-from a query parameter:</p>
-
-<table>
-<tr><th>Framework</th><th>Flat fields</th><th>Multi-level dots</th><th>Works 
on Map/List</th><th>No code changes</th></tr>
-<tr><td><strong>Axis2/Java</strong></td><td><strong>Yes</strong></td><td><strong>Yes</strong></td><td><strong>Yes</strong></td><td><strong>Yes</strong></td></tr>
-<tr><td>Spring + 
Jackson</td><td>Manual</td><td>No</td><td>No</td><td>No</td></tr>
-<tr><td>GraphQL</td><td colspan="4">Full field selection, but requires a 
different API architecture</td></tr>
-<tr><td>gRPC FieldMask</td><td>Yes</td><td>Yes</td><td>No (Protobuf 
only)</td><td>Yes</td></tr>
-<tr><td>Django REST + flex-fields</td><td>Yes</td><td>Partial (1 
level)</td><td>No</td><td>No</td></tr>
-<tr><td>FastAPI / 
Pydantic</td><td>Manual</td><td>No</td><td>No</td><td>No</td></tr>
-</table>
-
-<p>The closest competitor is gRPC's FieldMask, which supports dot-notation
-paths but requires Protocol Buffers — it cannot help existing JSON-RPC
-services. GraphQL provides full field selection but requires a completely
-different API contract. Spring and Django require custom serializer code
-for any field filtering beyond flat, annotated POJOs.</p>
-
-<p>Axis2/Java achieves this without annotations, schema changes, or new
-dependencies. The service returns whatever POJO or Map it already returns.
-The formatter intercepts at the serialization layer, filters during
-streaming, and the client receives only the fields it asked for.</p>
-
-<h3>Parity with Axis2/C</h3>
-
-<p>The Axis2/C implementation supports single-level dot-notation
-(<code>?fields=status,results.id</code>). Multi-level dot-notation is
-an Axis2/Java extension. Axis2/C uses json-c parse-and-delete;
-Axis2/Java uses reflection-based selective serialization. Both
-approaches produce identical filtered output for single-level queries.</p>
+<p>See the <a href="json-streaming-formatter.html">Streaming JSON Formatter</a>
+guide for complete documentation on <code>?fields=</code> query parameter
+filtering, including multi-level dot-notation for nested Maps and Collections,
+Java POJO examples, and competitive context.</p>
 
 </body>
 </html>
diff --git a/src/site/xdoc/docs/json-streaming-formatter.xml 
b/src/site/xdoc/docs/json-streaming-formatter.xml
index 8b296180cd..b4f5529f9d 100644
--- a/src/site/xdoc/docs/json-streaming-formatter.xml
+++ b/src/site/xdoc/docs/json-streaming-formatter.xml
@@ -101,22 +101,96 @@
 
         <subsection name="Field Selection (?fields=)">
         <p>When using <code>FieldFilteringMessageFormatter</code>, callers can
-        reduce response payload size by specifying which top-level fields to
-        include. This is useful for AI agents (MCP tools) and API consumers
-        that need only a subset of the response fields.</p>
-<source><![CDATA[# Only return status and portfolioVariance fields
-curl -sk --http2 \
-  
"https://host/services/FinancialBenchmarkService?fields=status,portfolioVariance";
 \
-  -H "Content-Type: application/json" \
-  -d '{"portfolioVariance":[{"arg0":{...}}]}'
-
-# Response: {"response":{"status":"SUCCESS","portfolioVariance":0.0245}}
-# (other fields like calcTimeUs, annualizedVolatility, etc. are 
omitted)]]></source>
-        <p>Field filtering happens during serialization — non-selected fields
-        are never serialized, never buffered, and never written to the wire.
-        The streaming pipeline (Moshi → Okio → FlushingOutputStream → HTTP/2
-        DATA frames) is preserved. When no <code>fields</code> parameter is
-        present, the formatter delegates directly with zero overhead.</p>
+        reduce response payload size by specifying which fields to include.
+        This is useful for AI agents (MCP tools), mobile clients, and API
+        consumers that need only a subset of the response.</p>
+
+        <p><b>Flat filtering</b> — select top-level response fields:</p>
+<source><![CDATA[GET /services/MyService?fields=status,result
+# Response: {"response":{"status":"SUCCESS","result":0.0245}}
+# (other top-level fields omitted)]]></source>
+
+        <p><b>Multi-level dot-notation</b> — filter inside nested objects and
+        collections. This is the key feature for services that return large
+        nested data structures.</p>
+
+        <p>Consider a service that returns a response POJO where the heavy
+        data lives inside a <code>Map&lt;String, Object&gt;</code> — common
+        when the service parses JSON from a backend into Java Collections
+        rather than typed POJOs:</p>
+
+<source><![CDATA[// The response POJO — what the Axis2 service method returns
+public class ServiceResponse {
+    private String status;
+    private long responseTimeMs;
+    private Map<String, Object> data;   // parsed from backend JSON
+}
+
+// At runtime, "data" contains:
+//   "records" -> List<Map<String, Object>>   (100+ fields per element)
+//   "metadata" -> Map<String, Object>
+//   "diagnostics" -> Map<String, Object>]]></source>
+
+        <p>The Java path from the response root to a record field maps
+        directly to the <code>?fields=</code> syntax:</p>
+
+<source><![CDATA[response.getData()                 // Map<String, Object>  = 
"data"
+    .get("records")                // List<Map>            = "data.records"
+    .get(0).get("id")              // Object               = "data.records.id"
+
+?fields=status,data.records.id,data.records.name
+   |       |         |          |
+   |       |         |          +-- 3rd level: key inside each List element
+   |       |         +------------- 2nd level: key inside the data Map
+   |       +----------------------- 1st level: field on the response POJO
+   +------------------------------- top-level POJO field (no dots)]]></source>
+
+        <p><b>Before</b> (5MB, 127 fields per record):</p>
+<source><![CDATA[{"response": {
+    "status": "SUCCESS",
+    "data": {
+        "records": [
+            {"id":"item-1", "name":"Widget A", ... 125 more fields ...},
+            {"id":"item-2", "name":"Widget B", ... 125 more fields ...}
+        ],
+        "metadata": {...},
+        "diagnostics": {...}
+    }
+}}]]></source>
+
+        <p><b>After</b> <code>?fields=status,data.records.id</code> (~150KB, 
97% reduction):</p>
+<source><![CDATA[{"response": {
+    "status": "SUCCESS",
+    "data": {
+        "records": [
+            {"id":"item-1"},
+            {"id":"item-2"}
+        ]
+    }
+}}]]></source>
+
+        <p>Filtering happens during serialization — excluded fields are never
+        serialized, never buffered, never written to the wire. The streaming
+        pipeline is preserved end-to-end. When no <code>fields</code> parameter
+        is present, the formatter delegates directly with zero overhead.</p>
+
+        <p>Multi-level dot-notation works on POJOs, Maps, and Collections —
+        including <code>Map&lt;String, Object&gt;</code> containing
+        <code>List&lt;Map&lt;String, Object&gt;&gt;</code>. Both Moshi and GSON
+        formatters support the same filtering behavior.</p>
+
+        <p><b>Competitive context:</b> No other Java or Python JSON web 
services
+        framework ships recursive multi-level field filtering that operates on
+        runtime Maps and Collections from a query parameter. The closest
+        comparable is gRPC FieldMask (Protobuf only) and GraphQL (requires a
+        different API architecture). Spring + Jackson, Django REST, and FastAPI
+        all require custom serializer code for nested field selection.
+        Axis2/Java achieves this with zero service-side code changes.</p>
+
+        <p><b>Limitation:</b> Field names containing a literal dot character
+        cannot be selected, as the dot is always interpreted as a nesting
+        delimiter. The Axis2/C implementation supports single-level
+        dot-notation only; multi-level is an Axis2/Java extension.</p>
         </subsection>
 
         <subsection name="Flush Interval Tuning (services.xml)">

Reply via email to