Skip to content

Commit 3f22a13

Browse files
authored
Fix #358: allow skipping 'xsi` attributes (#837)
1 parent bb3fd34 commit 3f22a13

5 files changed

Lines changed: 136 additions & 0 deletions

File tree

release-notes/CREDITS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ Tobias M (@lightbringer)
4747
Eduard Wirch (@ewirch)
4848
* Reported #306: Can not use `@JacksonXmlText` for Creator property (creator parameter)
4949
(3.2.0)
50+
* Requested #358: Allow skipping attributes from the `http://www.w3.org/2001/XMLSchema-instance`
51+
namespace (with new `XmlReadFeature.SKIP_UNKNOWN_XSI_ATTRIBUTES`)
52+
(3.2.0)
5053

5154
Ruven Chu (@Chubacca)
5255
* Reported #334: `@JacksonXmlElementWrapper(useWrapping=false)` fails when using

release-notes/VERSION

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Version: 3.x (for earlier see VERSION-2.x)
3333
a `DeserializerModifier` that delegates
3434
(reported by Ruven C)
3535
(fix by @cowtowncoder, w/ Claude code)
36+
#358: Allow skipping attributes from the `http://www.w3.org/2001/XMLSchema-instance`
37+
namespace (with new `XmlReadFeature.SKIP_UNKNOWN_XSI_ATTRIBUTES`)
38+
(requested by Eduard W)
39+
(fix by @cowtowncoder, w/ Claude code)
3640
#426: `InvalidTypeIdException` when parsing XML to POJO containing nested `List<>`,
3741
custom `TypeIdResolver`
3842
(reported by @ankagar)

src/main/java/tools/jackson/dataformat/xml/XmlReadFeature.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,24 @@ public enum XmlReadFeature implements FormatFeature
5555
*/
5656
PROCESS_XSI_NIL(true),
5757

58+
/**
59+
* Feature that controls whether XML Schema Instance (XSI) namespace attributes
60+
* other than {@code xsi:nil} and {@code xsi:type} (which have their own handling
61+
* via {@link #PROCESS_XSI_NIL} and {@link #AUTO_DETECT_XSI_TYPE}) are silently
62+
* skipped during deserialization. Attributes affected include {@code xsi:schemaLocation}
63+
* and {@code xsi:noNamespaceSchemaLocation}.
64+
*<p>
65+
* When enabled, these attributes are ignored and will not cause
66+
* {@code UnrecognizedPropertyException}. When disabled (default), they are
67+
* exposed as regular attributes and may require matching POJO properties
68+
* or {@code DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES} to be disabled.
69+
*<p>
70+
* Default setting is {@code false}.
71+
*
72+
* @since 3.2
73+
*/
74+
SKIP_UNKNOWN_XSI_ATTRIBUTES(false),
75+
5876
;
5977

6078
private final boolean _defaultState;

src/main/java/tools/jackson/dataformat/xml/deser/XmlTokenStream.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ public class XmlTokenStream
7979

8080
protected final boolean _cfgProcessXsiType;
8181

82+
// [dataformat-xml#358]
83+
protected final boolean _cfgSkipUnknownXsiAttributes;
84+
8285
protected XmlNameProcessor _nameProcessor;
8386

8487
/*
@@ -183,6 +186,7 @@ public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef,
183186
_formatFeatures = formatFeatures;
184187
_cfgProcessXsiNil = XmlReadFeature.PROCESS_XSI_NIL.enabledIn(_formatFeatures);
185188
_cfgProcessXsiType = XmlReadFeature.AUTO_DETECT_XSI_TYPE.enabledIn(_formatFeatures);
189+
_cfgSkipUnknownXsiAttributes = XmlReadFeature.SKIP_UNKNOWN_XSI_ATTRIBUTES.enabledIn(_formatFeatures);
186190
// 04-Dec-2023, tatu: [dataformat-xml#618] Need further customized adapter:
187191
_xmlReader = Stax2JacksonReaderAdapter.wrapIfNecessary(xmlReader);
188192
_nameProcessor = nameProcessor;
@@ -487,6 +491,23 @@ private final int _next() throws XMLStreamException
487491
//System.out.println(" XmlTokenStream._next(): Got xsi:nil, skipping element");
488492
return _handleEndElement();
489493
}
494+
// [dataformat-xml#358]: Optionally skip XSI namespace attributes other
495+
// than "type" (handled by _decodeAttributeName) and "nil" (when xsi:nil
496+
// processing is disabled, it should be exposed as a regular attribute)
497+
if (_cfgSkipUnknownXsiAttributes) {
498+
while (_nextAttributeIndex < _attributeCount) {
499+
final String attrNs = _xmlReader.getAttributeNamespace(_nextAttributeIndex);
500+
if (XSI_NAMESPACE.equals(attrNs)) {
501+
final String localName = _xmlReader.getAttributeLocalName(_nextAttributeIndex);
502+
if (!"type".equals(localName)
503+
&& !(!_cfgProcessXsiNil && "nil".equals(localName))) {
504+
++_nextAttributeIndex;
505+
continue;
506+
}
507+
}
508+
break;
509+
}
510+
}
490511
if (_nextAttributeIndex < _attributeCount) {
491512
//System.out.println(" XmlTokenStream._next(): Got attr(s)!");
492513
_decodeAttributeName(_xmlReader.getAttributeNamespace(_nextAttributeIndex),
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package tools.jackson.dataformat.xml.deser;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import tools.jackson.dataformat.xml.*;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
// [dataformat-xml#358]: XSI namespace attributes like schemaLocation
10+
// should be silently ignored when SKIP_UNKNOWN_XSI_ATTRIBUTES is enabled
11+
public class XsiSchemaLocationTest extends XmlTestUtil
12+
{
13+
static class Dto {
14+
public String value;
15+
}
16+
17+
static class DtoWithAttr {
18+
public String value;
19+
public int count;
20+
}
21+
22+
static class DtoWithSchemaLocation {
23+
public String value;
24+
public String schemaLocation;
25+
}
26+
27+
private final XmlMapper MAPPER_SKIP = mapperBuilder()
28+
.enable(XmlReadFeature.SKIP_UNKNOWN_XSI_ATTRIBUTES)
29+
.build();
30+
31+
private final XmlMapper MAPPER_DEFAULT = mapperBuilder().build();
32+
33+
// Basic case from issue #358: xsi:schemaLocation should be ignored when enabled
34+
@Test
35+
public void testXsiSchemaLocationIgnored() throws Exception
36+
{
37+
Dto result = MAPPER_SKIP.readValue(
38+
"<dto xmlns=\"http://SomeNamespace\""
39+
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
40+
+ " xsi:schemaLocation=\"http://SomeNamespace"
41+
+ " http://SomeNamespace/schema.xsd\">"
42+
+ "<value>hello</value>"
43+
+ "</dto>",
44+
Dto.class);
45+
assertEquals("hello", result.value);
46+
}
47+
48+
// Also test xsi:noNamespaceSchemaLocation
49+
@Test
50+
public void testXsiNoNamespaceSchemaLocationIgnored() throws Exception
51+
{
52+
Dto result = MAPPER_SKIP.readValue(
53+
"<dto xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
54+
+ " xsi:noNamespaceSchemaLocation=\"schema.xsd\">"
55+
+ "<value>world</value>"
56+
+ "</dto>",
57+
Dto.class);
58+
assertEquals("world", result.value);
59+
}
60+
61+
// Ensure regular attributes still work alongside XSI attributes
62+
@Test
63+
public void testXsiSchemaLocationWithRegularAttributes() throws Exception
64+
{
65+
DtoWithAttr result = MAPPER_SKIP.readValue(
66+
"<DtoWithAttr xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
67+
+ " xsi:schemaLocation=\"http://SomeNamespace schema.xsd\""
68+
+ " count=\"42\">"
69+
+ "<value>test</value>"
70+
+ "</DtoWithAttr>",
71+
DtoWithAttr.class);
72+
assertEquals("test", result.value);
73+
assertEquals(42, result.count);
74+
}
75+
76+
// When feature is disabled (default), XSI attributes should be exposed
77+
// and bindable to POJO properties
78+
@Test
79+
public void testXsiSchemaLocationExposedByDefault() throws Exception
80+
{
81+
DtoWithSchemaLocation result = MAPPER_DEFAULT.readValue(
82+
"<DtoWithSchemaLocation xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
83+
+ " xsi:schemaLocation=\"http://SomeNamespace schema.xsd\">"
84+
+ "<value>bound</value>"
85+
+ "</DtoWithSchemaLocation>",
86+
DtoWithSchemaLocation.class);
87+
assertEquals("bound", result.value);
88+
assertEquals("http://SomeNamespace schema.xsd", result.schemaLocation);
89+
}
90+
}

0 commit comments

Comments
 (0)