Skip to content

Commit 74cde13

Browse files
committed
Start work on #324
1 parent 2ecbbcf commit 74cde13

File tree

3 files changed

+108
-20
lines changed

3 files changed

+108
-20
lines changed

src/main/java/com/fasterxml/jackson/dataformat/xml/XmlTypeResolverBuilder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public XmlTypeResolverBuilder(JsonTypeInfo.Value settings) {
3434
@Override
3535
public StdTypeResolverBuilder init(JsonTypeInfo.Id idType, TypeIdResolver idRes)
3636
{
37-
3837
super.init(idType, idRes);
3938
if (_typeProperty != null) {
4039
_typeProperty = StaxUtil.sanitizeXmlTypeName(_typeProperty);
@@ -138,6 +137,8 @@ protected static String decodeXmlClassName(String className)
138137
protected static class XmlClassNameIdResolver
139138
extends ClassNameIdResolver
140139
{
140+
private static final long serialVersionUID = 1L;
141+
141142
public XmlClassNameIdResolver(JavaType baseType, TypeFactory typeFactory,
142143
PolymorphicTypeValidator ptv)
143144
{
@@ -159,6 +160,8 @@ public JavaType typeFromId(DatabindContext context, String id) throws IOExceptio
159160
protected static class XmlMinimalClassNameIdResolver
160161
extends MinimalClassNameIdResolver
161162
{
163+
private static final long serialVersionUID = 1L;
164+
162165
public XmlMinimalClassNameIdResolver(JavaType baseType, TypeFactory typeFactory,
163166
PolymorphicTypeValidator ptv)
164167
{

src/main/java/com/fasterxml/jackson/dataformat/xml/ser/ToXmlGenerator.java

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,19 @@ public enum Feature implements FormatFeature
9393
* @since 2.13
9494
*/
9595
UNWRAP_ROOT_OBJECT_NODE(false),
96+
97+
/**
98+
* Feature that enables automatic conversion of logical property
99+
* name {@code "xsi:type"} into matching XML name where "type"
100+
* is the local name and "xsi" prefix is bound to URI
101+
* {@link XMLConstants#W3C_XML_SCHEMA_INSTANCE_NS_URI},
102+
* and output is indicated to be done as XML Attribute.
103+
* This is mostly desirable for Polymorphic handling where it is difficult
104+
* to specify XML Namespace for type identifier
105+
*
106+
* @since 2.17
107+
*/
108+
AUTO_DETECT_XSI_TYPE(false),
96109
;
97110

98111
final boolean _defaultState;
@@ -247,20 +260,26 @@ public void initGenerator() throws IOException
247260
}
248261
_initialized = true;
249262
try {
263+
boolean xmlDeclWritten;
250264
if (Feature.WRITE_XML_1_1.enabledIn(_formatFeatures)) {
251265
_xmlWriter.writeStartDocument("UTF-8", "1.1");
266+
xmlDeclWritten = true;
252267
} else if (Feature.WRITE_XML_DECLARATION.enabledIn(_formatFeatures)) {
253268
_xmlWriter.writeStartDocument("UTF-8", "1.0");
269+
xmlDeclWritten = true;
254270
} else {
255-
return;
271+
xmlDeclWritten = false;
256272
}
257273
// as per [dataformat-xml#172], try adding indentation
258-
if (_xmlPrettyPrinter != null) {
274+
if (xmlDeclWritten && (_xmlPrettyPrinter != null)) {
259275
// ... but only if it is likely to succeed:
260276
if (!_stax2Emulation) {
261277
_xmlPrettyPrinter.writePrologLinefeed(_xmlWriter);
262278
}
263279
}
280+
if (Feature.AUTO_DETECT_XSI_TYPE.enabledIn(_formatFeatures)) {
281+
_xmlWriter.setPrefix("xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
282+
}
264283
} catch (XMLStreamException e) {
265284
StaxUtil.throwAsGenerationException(e, this);
266285
}
@@ -487,23 +506,35 @@ public void writeRepeatedFieldName() throws IOException
487506
/* JsonGenerator method overrides
488507
/**********************************************************
489508
*/
490-
491-
/* Most overrides in this section are just to make methods final,
492-
* to allow better inlining...
493-
*/
509+
510+
@Override
511+
public void writeFieldName(SerializableString name) throws IOException
512+
{
513+
writeFieldName(name.getValue());
514+
}
494515

495516
@Override
496517
public final void writeFieldName(String name) throws IOException
497518
{
498519
if (_writeContext.writeFieldName(name) == JsonWriteContext.STATUS_EXPECT_VALUE) {
499520
_reportError("Can not write a field name, expecting a value");
500521
}
501-
// Should this ever get called?
502-
String ns = (_nextName == null) ? "" : _nextName.getNamespaceURI();
503-
_nameToEncode.namespace = ns;
504-
_nameToEncode.localPart = name;
505-
_nameProcessor.encodeName(_nameToEncode);
506-
setNextName(new QName(_nameToEncode.namespace, _nameToEncode.localPart));
522+
523+
String ns;
524+
// 30-Jan-2024, tatu: Surprise!
525+
if (Feature.AUTO_DETECT_XSI_TYPE.enabledIn(_formatFeatures)
526+
&& "xsi:type".equals(name)) {
527+
setNextName(new QName(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI,
528+
"type", "xsi"));
529+
setNextIsAttribute(true);
530+
} else {
531+
// Should this ever get called?
532+
ns = (_nextName == null) ? "" : _nextName.getNamespaceURI();
533+
_nameToEncode.namespace = ns;
534+
_nameToEncode.localPart = name;
535+
_nameProcessor.encodeName(_nameToEncode);
536+
setNextName(new QName(_nameToEncode.namespace, _nameToEncode.localPart));
537+
}
507538
}
508539

509540
@Override
@@ -519,7 +550,7 @@ public final void writeStringField(String fieldName, String value) throws IOExce
519550
// handling...
520551
//
521552
// See [dataformat-xml#4] for more context.
522-
553+
523554
/*
524555
// @since 2.9
525556
public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException
@@ -640,12 +671,6 @@ public final void _handleEndObject() throws IOException
640671
/**********************************************************
641672
*/
642673

643-
@Override
644-
public void writeFieldName(SerializableString name) throws IOException
645-
{
646-
writeFieldName(name.getValue());
647-
}
648-
649674
@Override
650675
public void writeString(String text) throws IOException
651676
{
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.fasterxml.jackson.dataformat.xml.ser;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonRootName;
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
6+
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
7+
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
8+
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
9+
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
10+
11+
// [dataformat-xml#324]
12+
public class XsiTypeWriteTest extends XmlTestBase
13+
{
14+
@JsonRootName("Typed")
15+
static class TypeBean {
16+
@JsonProperty("xsi:type")
17+
public String typeId = "abc";
18+
}
19+
20+
@JsonRootName("Poly")
21+
@JsonTypeInfo(use = Id.SIMPLE_NAME, include = As.PROPERTY, property="xsi:type")
22+
static class PolyBean {
23+
public int value = 42;
24+
}
25+
26+
private final XmlMapper NO_XSI_MAPPER = XmlMapper.builder()
27+
.configure(ToXmlGenerator.Feature.AUTO_DETECT_XSI_TYPE, false)
28+
.build();
29+
30+
private final XmlMapper XSI_ENABLED_MAPPER = XmlMapper.builder()
31+
.configure(ToXmlGenerator.Feature.AUTO_DETECT_XSI_TYPE, true)
32+
.build();
33+
34+
public void testExplicitXsiTypeWriteDisabled() throws Exception
35+
{
36+
assertEquals("<Typed><xsi:type>abc</xsi:type></Typed>",
37+
NO_XSI_MAPPER.writeValueAsString(new TypeBean()));
38+
}
39+
40+
public void testExplicitXsiTypeWriteEnabled() throws Exception
41+
{
42+
assertEquals(
43+
a2q("<Typed xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='abc'/>"),
44+
a2q(XSI_ENABLED_MAPPER.writeValueAsString(new TypeBean())));
45+
}
46+
47+
public void testXsiTypeAsTypeIdWriteDisabled() throws Exception
48+
{
49+
assertEquals("<Poly><xsi_type>abc</xsi_type><value>42</value></Poly>",
50+
NO_XSI_MAPPER.writeValueAsString(new PolyBean()));
51+
}
52+
53+
public void testXsiTypeAsTypeIdWriteEnabled() throws Exception
54+
{
55+
assertEquals(
56+
a2q("<Poly xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='abc'>"
57+
+"<value>42</value></Poly>"),
58+
a2q(XSI_ENABLED_MAPPER.writeValueAsString(new PolyBean())));
59+
}
60+
}

0 commit comments

Comments
 (0)