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 d332bfce5e Add view-based projection (@JsonView analog) to bean 
marshalling; adopt isEmpty utility across the reactor.
d332bfce5e is described below

commit d332bfce5e8a0d2c7d5e9d2de30ad0896aeb5c41
Author: James Bognar <[email protected]>
AuthorDate: Tue Jun 16 13:12:06 2026 -0400

    Add view-based projection (@JsonView analog) to bean marshalling; adopt 
isEmpty utility across the reactor.
---
 pages/release-notes/10.0.0.md           |  48 +++++++++
 pages/topics/02.04.12.ViewProjection.md | 182 ++++++++++++++++++++++++++++++++
 pages/topics/02.20.JacksonComparison.md |   1 +
 sidebars.ts                             |  19 ++--
 4 files changed, 243 insertions(+), 7 deletions(-)

diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index 87452d8a85..a4cbda460a 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -30,6 +30,54 @@ No functional behavior was changed as part of this cleanup; 
all changes were pur
 
 ### New Features
 
+### juneau-marshall / juneau-commons
+
+#### View-based property projection (`@MarshalledProp(view=...)`)
+
+Juneau now supports **view-based projection**: declare named views on bean 
properties and select an active
+view per serialize/parse call so only the in-view properties are emitted or 
consumed.  This is the Juneau
+equivalent of Jackson's `@JsonView` / `MapperFeature.DEFAULT_VIEW_INCLUSION`.
+
+Tag a property with one or more string view names via the new `view` member on 
`@MarshalledProp`:
+
+```java
+public class Person {
+    public String id;                         // untagged — visible in every 
view
+
+    @MarshalledProp(view = "summary")
+    public String name;
+
+    @MarshalledProp(view = "detail")
+    public String description;
+}
+```
+
+Activate a view on any serializer or parser builder, or override it per call 
on a session:
+
+```java
+// Context-level default:
+var s = Json5Serializer.DEFAULT.copy().activeView("summary").build();
+s.serialize(person);  // → {id:'1',name:'Alice'}
+
+// Per-call session override:
+s.createSession().activeView("detail").build().serialize(person);  // → 
{description:'A person',id:'1'}
+```
+
+Key behaviours:
+- **Default-view policy:** untagged properties are included in all views by 
default (mirrors Jackson's
+  `DEFAULT_VIEW_INCLUSION=true`). Call `disableDefaultViewInclusion()` to 
reverse.
+- **Union semantics:** `@MarshalledProp(view = {"A","B"})` makes a property 
visible when `A` *or* `B` is active.
+- **Flat string names:** no marker-class hierarchy required.
+- **Parse-side:** out-of-view input properties are routed through the existing 
unknown/ignored-property
+  mechanism (silently ignored by default; `ignoreUnknownBeanProperties(false)` 
makes them throw).
+- **Mix-in support:** view membership can be applied to unmodifiable classes 
via `@MarshalledPropApply` /
+  `applyAnnotations()`, just like any other `@MarshalledProp` attribute.
+- **Cross-format:** projection is model-level and behaves identically across 
JSON, XML, HTML, MsgPack, and
+  all other Juneau formats.
+
+See the [View-Based Projection](/docs/topics/ViewProjection) topic page for 
full documentation and a
+Jackson migration table.
+
 ### juneau-microservice-jetty
 
 #### `JettyMicroservice` zero-config facade + bundled defaults
diff --git a/pages/topics/02.04.12.ViewProjection.md 
b/pages/topics/02.04.12.ViewProjection.md
new file mode 100644
index 0000000000..2da73099b0
--- /dev/null
+++ b/pages/topics/02.04.12.ViewProjection.md
@@ -0,0 +1,182 @@
+---
+title: "View-Based Projection"
+slug: ViewProjection
+---
+
+View-based projection lets you define named **views** — property subsets — on 
a bean and choose which view
+is active per serialize/parse call. Only the properties belonging to the 
active view are emitted on
+serialization; on parsing, out-of-view properties are silently discarded (or 
rejected, depending on your
+unknown-property policy).
+
+This is the Juneau equivalent of Jackson's `@JsonView` / 
`MapperFeature.DEFAULT_VIEW_INCLUSION`.
+
+---
+
+## Declaring view membership on properties
+
+Use the
+<a 
href="/site/apidocs/org/apache/juneau/annotation/MarshalledProp.html#view()" 
target="_blank">@MarshalledProp(view)</a>
+member to tag a property with one or more string view names:
+
+```java
+public class Person {
+
+    // Untagged — visible in every view (default policy).
+    public String id;
+
+    // Visible only when the "summary" view is active.
+    @MarshalledProp(view = "summary")
+    public String name;
+
+    // Visible only when the "detail" view is active.
+    @MarshalledProp(view = "detail")
+    public String description;
+
+    // Visible in EITHER "summary" OR "detail" (union semantics).
+    @MarshalledProp(view = {"summary", "detail"})
+    public String email;
+}
+```
+
+View names are plain strings. No marker-class hierarchy is required.
+
+---
+
+## Activating a view on a serializer or parser
+
+Call
+<a 
href="/site/apidocs/org/apache/juneau/MarshallingContext.Builder.html#activeView(java.lang.String)"
 target="_blank">activeView(String)</a>
+on any serializer or parser builder to set a context-level default:
+
+```java
+// Serialize with the "summary" view.
+var s = Json5Serializer.DEFAULT.copy().activeView("summary").build();
+s.serialize(new Person());
+// → {id:'1',name:'Alice',email:'[email protected]'}
+```
+
+To override the view for a single call without rebuilding the serializer, use
+<a 
href="/site/apidocs/org/apache/juneau/MarshallingSession.Builder.html#activeView(java.lang.String)"
 target="_blank">activeView(String)</a>
+on a session:
+
+```java
+var session = serializer.createSession().activeView("detail").build();
+session.serialize(new Person());
+// → {description:'A person',email:'[email protected]',id:'1'}
+```
+
+Passing `null` clears the active view for that session, making all view-tagged 
properties visible again.
+
+---
+
+## Default-view policy: untagged properties
+
+When an active view is set, **untagged properties are included by default** — 
this matches Jackson's
+`MapperFeature.DEFAULT_VIEW_INCLUSION` default and the principle of least 
surprise: only explicitly-tagged
+properties become view-restricted.
+
+To flip the policy so that only tagged properties are visible, call
+<a 
href="/site/apidocs/org/apache/juneau/MarshallingContext.Builder.html#disableDefaultViewInclusion()"
 target="_blank">disableDefaultViewInclusion()</a>:
+
+```java
+// Strict mode: only tagged properties appear.
+var s = Json5Serializer.DEFAULT.copy()
+    .activeView("summary")
+    .disableDefaultViewInclusion()
+    .build();
+s.serialize(new Person());
+// → {email:'[email protected]',name:'Alice'}   (id omitted — it was untagged)
+```
+
+---
+
+## View semantics
+
+- **Flat string names.** No class-hierarchy inheritance; a view named 
`"summary"` has no relationship to
+  any other view.
+- **Union membership.** A property tagged `{A, B}` is visible whenever the 
active view is `A` **or** `B`.
+  There is no intersection requirement.
+- **No active view = all properties visible.** If no `activeView()` is set (or 
it is set to `null`), the
+  feature is off and every property is serialized as normal.
+
+---
+
+## View membership on unmodifiable classes (`*Config` / `@ContextApply`)
+
+If you cannot annotate a class directly (third-party, generated, or sealed), 
apply view membership via the
+<a href="/site/apidocs/org/apache/juneau/annotation/MarshalledPropApply.html" 
target="_blank">@MarshalledPropApply</a>
+`*Config` mechanism:
+
+```java
+// Config annotation — does not require modifying ThirdPartyBean.
+@MarshalledPropApply(on = "ThirdPartyBean.name",        value = 
@MarshalledProp(view = "summary"))
+@MarshalledPropApply(on = "ThirdPartyBean.description", value = 
@MarshalledProp(view = "detail"))
+public static class ThirdPartyBeanViewConfig {}
+
+// Apply the config when building the serializer.
+var s = Json5Serializer.DEFAULT.copy()
+    .applyAnnotations(ThirdPartyBeanViewConfig.class)
+    .activeView("summary")
+    .build();
+```
+
+---
+
+## Parse-side behavior
+
+When parsing, properties that are **not** in the active view are treated as 
unknown properties.  By default
+Juneau silently discards unknown properties, so out-of-view input is quietly 
ignored:
+
+```java
+var p = Json5Parser.DEFAULT.copy().activeView("summary").build();
+// description is not in the "summary" view → ignored (default behavior).
+Person person = p.parse("{id:'1',name:'Alice',description:'ignored'}", 
Person.class);
+```
+
+To make out-of-view input throw an exception, disable the unknown-property 
tolerance with
+<a href="/site/apidocs/org/apache/juneau/MarshallingContext.Builder.html" 
target="_blank">ignoreUnknownBeanProperties(false)</a>:
+
+```java
+var p = Json5Parser.DEFAULT.copy()
+    .activeView("summary")
+    .ignoreUnknownBeanProperties(false)  // strict: out-of-view input throws
+    .build();
+p.parse("{id:'1',description:'oops'}", Person.class);  // throws ParseException
+```
+
+---
+
+## Precedence with other property controls
+
+When multiple property-control mechanisms interact, the precedence is:
+
+1. `@MarshalledIgnore` / `@BeanIgnore` — hard exclude (wins over everything).
+2. `readOnly` / `writeOnly` — direction filter.
+3. Active view — membership filter.
+4. `beanProperties` / `beanPropertiesExcludes` — further narrowing.
+5. Swaps / value-level inclusion knobs — applied to surviving properties.
+
+Views and `beanProperties*` compose by **intersection**: a property must pass 
both filters to appear.
+
+---
+
+## Cross-format invariance
+
+View selection lives in `MarshallingContext` / `BeanPropertyMeta` — the bean 
model layer — not in any
+format-specific serializer.  The same `activeView()` call produces consistent 
projection across JSON, XML,
+HTML, MsgPack, YAML, and every other Juneau format.
+
+---
+
+## Migration from Jackson `@JsonView`
+
+| Jackson | Juneau |
+|---------|--------|
+| `@JsonView(MyView.class)` on a property | `@MarshalledProp(view = "MyView")` 
|
+| `@JsonView({A.class, B.class})` | `@MarshalledProp(view = {"A", "B"})` |
+| `objectMapper.writerWithView(MyView.class).writeValue(...)` | 
`serializer.createSession().activeView("MyView").build().serialize(...)` |
+| `objectMapper.readerWithView(MyView.class).readValue(...)` | 
`parser.createSession().activeView("MyView").build().parse(...)` |
+| `MapperFeature.DEFAULT_VIEW_INCLUSION = true` (default) | default — untagged 
properties included |
+| `MapperFeature.DEFAULT_VIEW_INCLUSION = false` | 
`serializer.copy().activeView(...).disableDefaultViewInclusion().build()` |
+| Marker-class view tokens (`class SummaryView {}`) | Not needed — Juneau uses 
plain strings |
+| View inheritance (`class DetailView extends SummaryView`) | No equivalent — 
Juneau views are flat names; list both: `view = {"summary", "detail"}` |
diff --git a/pages/topics/02.20.JacksonComparison.md 
b/pages/topics/02.20.JacksonComparison.md
index f36f9fa929..25faa57a7a 100644
--- a/pages/topics/02.20.JacksonComparison.md
+++ b/pages/topics/02.20.JacksonComparison.md
@@ -24,3 +24,4 @@ The following charts describe equivalent features between the 
two libraries:
 | `@JsonInclude` | No equivalent annotation but can be controlled via various 
settings:<br/><a href="/site/apidocs/org/apache/juneau/MarshallingContext.html" 
target="_blank">MarshallingContext</a><br/><a 
href="/site/apidocs/org/apache/juneau/marshall/serializer/Serializer.html" 
target="_blank">Serializer</a> |
 | `@JsonPropertyOrder` | <a 
href="/site/apidocs/org/apache/juneau/commons/bean/BeanType.html#properties()" 
target="_blank">@BeanType(properties="...")</a> |
 | `@JsonValue`<br/>`@JsonRawValue` | Can be replicated using swaps with 
`Reader` swapped values. |
+| `@JsonView(MyView.class)` | <a 
href="/site/apidocs/org/apache/juneau/annotation/MarshalledProp.html#view()" 
target="_blank">@MarshalledProp(view="MyView")</a> — see [View-Based 
Projection](/docs/topics/ViewProjection) |
diff --git a/sidebars.ts b/sidebars.ts
index 568cab0e4b..445ae49b74 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -171,13 +171,18 @@ const sidebars: SidebarsConfig = {
                                                                        id: 
'topics/02.04.10.PojoBuilders',
                                                                        label: 
'2.4.10. POJO Builders',
                                                                },
-                                                               {
-                                                                       type: 
'doc',
-                                                                       id: 
'topics/02.04.11.BypassSerialization',
-                                                                       label: 
'2.4.10. Bypass Serialization using `Readers` and `InputStreams`',
-                                                               },
-                                                       ],
-                                               },
+                                                       {
+                                                               type: 'doc',
+                                                               id: 
'topics/02.04.11.BypassSerialization',
+                                                               label: '2.4.11. 
Bypass Serialization using `Readers` and `InputStreams`',
+                                                       },
+                                                       {
+                                                               type: 'doc',
+                                                               id: 
'topics/02.04.12.ViewProjection',
+                                                               label: '2.4.12. 
View-Based Projection',
+                                                       },
+                                               ],
+                                       },
                                                {
                                                        type: 'doc',
                                                        id: 
'topics/02.05.HttpPartSerializersParsers',

Reply via email to