Skip to content

Commit 088e6bf

Browse files
committed
Fix generating invalid XML tag names
This commit introduces the `PROCESS_ESCAPED_MALFORMED_TAGS` and `ESCAPE_MALFORMED_TAGS` features that control whether invalid tag names will be escaped with an attribute. fixes #523 fixes #524
1 parent f406e23 commit 088e6bf

File tree

4 files changed

+261
-29
lines changed

4 files changed

+261
-29
lines changed

src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.fasterxml.jackson.dataformat.xml.PackageVersion;
2121
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
22+
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
2223
import com.fasterxml.jackson.dataformat.xml.util.CaseInsensitiveNameSet;
2324
import com.fasterxml.jackson.dataformat.xml.util.StaxUtil;
2425

@@ -82,6 +83,15 @@ public enum Feature implements FormatFeature
8283
*/
8384
PROCESS_XSI_NIL(true),
8485

86+
/**
87+
* Feature that controls whether the escaping mechanism from the
88+
* {@code ESCAPE_MALFORMED_TAGS} from {@link ToXmlGenerator.Feature}
89+
* is reversed.
90+
*
91+
* @since 2.14
92+
*/
93+
PROCESS_ESCAPED_MALFORMED_TAGS(true)
94+
8595
// 16-Nov-2020, tatu: would have been nice to add in 2.12 but is not
8696
// trivial to implement... so leaving out for now
8797

src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.fasterxml.jackson.dataformat.xml.deser;
22

33
import java.io.IOException;
4+
import java.util.Objects;
45

56
import javax.xml.XMLConstants;
7+
import javax.xml.namespace.QName;
68
import javax.xml.stream.*;
79

10+
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
811
import org.codehaus.stax2.XMLStreamLocation2;
912
import org.codehaus.stax2.XMLStreamReader2;
1013
import org.codehaus.stax2.ri.Stax2ReaderAdapter;
@@ -73,6 +76,8 @@ public class XmlTokenStream
7376

7477
protected boolean _cfgProcessXsiNil;
7578

79+
protected boolean _cfgProcessEscapedTag;
80+
7681
/*
7782
/**********************************************************************
7883
/* Parsing state
@@ -158,6 +163,7 @@ public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef,
158163
_sourceReference = sourceRef;
159164
_formatFeatures = formatFeatures;
160165
_cfgProcessXsiNil = FromXmlParser.Feature.PROCESS_XSI_NIL.enabledIn(_formatFeatures);
166+
_cfgProcessEscapedTag = FromXmlParser.Feature.PROCESS_ESCAPED_MALFORMED_TAGS.enabledIn(_formatFeatures);
161167
_xmlReader = Stax2ReaderAdapter.wrapIfNecessary(xmlReader);
162168
}
163169

@@ -176,7 +182,7 @@ public int initialize() throws XMLStreamException
176182
_localName = _xmlReader.getLocalName();
177183
_namespaceURI = _xmlReader.getNamespaceURI();
178184

179-
_checkXsiAttributes(); // sets _attributeCount, _nextAttributeIndex
185+
_checkMagicAttributes(); // sets _attributeCount, _nextAttributeIndex
180186

181187
// 02-Jul-2020, tatu: Two choices: if child elements OR attributes, expose
182188
// as Object value; otherwise expose as Text
@@ -220,6 +226,7 @@ public XMLStreamReader2 getXmlReader() {
220226
protected void setFormatFeatures(int f) {
221227
_formatFeatures = f;
222228
_cfgProcessXsiNil = FromXmlParser.Feature.PROCESS_XSI_NIL.enabledIn(f);
229+
_cfgProcessEscapedTag = FromXmlParser.Feature.PROCESS_ESCAPED_MALFORMED_TAGS.enabledIn(f);
223230
}
224231

225232
/*
@@ -618,17 +625,14 @@ private final String _getText(XMLStreamReader2 r) throws XMLStreamException
618625

619626
private final int _initStartElement() throws XMLStreamException
620627
{
621-
final String ns = _xmlReader.getNamespaceURI();
622-
final String localName = _xmlReader.getLocalName();
623-
624-
_checkXsiAttributes();
628+
final QName qName = _checkMagicAttributes();
625629

626630
// Support for virtual wrapping: in wrapping, may either create a new
627631
// wrapper scope (if in sub-tree, or matches wrapper element itself),
628632
// or implicitly close existing scope.
629633

630634
if (_currentWrapper != null) {
631-
if (_currentWrapper.matchesWrapper(localName, ns)) {
635+
if (_currentWrapper.matchesWrapper(qName.getLocalPart(), qName.getNamespaceURI())) {
632636
_currentWrapper = _currentWrapper.intermediateWrapper();
633637
//System.out.println(" _initStartElement(): START_ELEMENT ("+localName+") DOES match ["+_currentWrapper+"]: leave/add intermediate");
634638
} else {
@@ -638,23 +642,28 @@ private final int _initStartElement() throws XMLStreamException
638642
_namespaceURI = _currentWrapper.getWrapperNamespace();
639643
_currentWrapper = _currentWrapper.getParent();
640644
// Important! We also need to restore the START_ELEMENT, so:
641-
_nextLocalName = localName;
642-
_nextNamespaceURI = ns;
645+
_nextLocalName = qName.getLocalPart();
646+
_nextNamespaceURI = qName.getNamespaceURI();
643647
_repeatElement = REPLAY_START_DELAYED;
644648
return (_currentState = XML_END_ELEMENT);
645649
}
646650
}
647-
_localName = localName;
648-
_namespaceURI = ns;
651+
_localName = qName.getLocalPart();
652+
_namespaceURI = qName.getNamespaceURI();
649653
return (_currentState = XML_START_ELEMENT);
650654
}
651655

652656
/**
653657
* @since 2.10
654658
*/
655-
private final void _checkXsiAttributes() {
659+
protected QName _checkMagicAttributes() {
656660
int count = _xmlReader.getAttributeCount();
657661
_attributeCount = count;
662+
_nextAttributeIndex = 0;
663+
_xsiNilFound = false;
664+
665+
String ns = _xmlReader.getNamespaceURI();
666+
String localName = _xmlReader.getLocalName();
658667

659668
// [dataformat-xml#354]: xsi:nul handling; at first only if first attribute
660669
if (count >= 1) {
@@ -666,13 +675,21 @@ private final void _checkXsiAttributes() {
666675
_nextAttributeIndex = 1;
667676
// but only mark as nil marker if enabled
668677
_xsiNilFound = "true".equals(_xmlReader.getAttributeValue(0));
669-
return;
670678
}
671679
}
672680
}
673681

674-
_nextAttributeIndex = 0;
675-
_xsiNilFound = false;
682+
// Handle PROCESS_ESCAPED_MALFORMED_TAGS
683+
if (count > _nextAttributeIndex && _cfgProcessEscapedTag && localName.equals(ToXmlGenerator.ESCAPED_TAG_NAME)) {
684+
final String attr = _xmlReader.getAttributeLocalName(_nextAttributeIndex);
685+
final String attrNS = _xmlReader.getAttributeNamespace(_nextAttributeIndex);
686+
if (Objects.equals(attrNS, ToXmlGenerator.ESCAPED_ATTR_NS) && Objects.equals(attr, ToXmlGenerator.ESCAPED_ATTR_NAME)) {
687+
localName = _xmlReader.getAttributeValue(_nextAttributeIndex);
688+
_nextAttributeIndex += 1;
689+
}
690+
}
691+
692+
return new QName(ns, localName);
676693
}
677694

678695
/**

0 commit comments

Comments
 (0)