48

Parsing an XML file using the Java DOM parser results in:

[Fatal Error] os__flag_8c.xml:103:135: An invalid XML character (Unicode: 0xc) was found in the element content of the document.
org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0xc) was found in the element content of the document.
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source)
    at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
030
  • 10,842
  • 12
  • 78
  • 123
Ashish
  • 1,527
  • 4
  • 17
  • 33

11 Answers11

49

There are a few characters that are dissallowed in XML documents, even when you encapsulate data in CDATA-blocks.

If you generated the document you will need to entity encode it or strip it out. If you have an errorneous document, you should strip away these characters before trying to parse it.

See dolmens answer in this thread: Invalid Characters in XML

Where he links to this article: http://www.w3.org/TR/xml/#charsets

Basically, all characters below 0x20 is disallowed, except 0x9 (TAB), 0xA (CR?), 0xD (LF?)

Community
  • 1
  • 1
jishi
  • 24,126
  • 6
  • 49
  • 75
21
public String stripNonValidXMLCharacters(String in) {
    StringBuffer out = new StringBuffer(); // Used to hold the output.
    char current; // Used to reference the current character.

    if (in == null || ("".equals(in))) return ""; // vacancy test.
    for (int i = 0; i < in.length(); i++) {
        current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen.
        if ((current == 0x9) ||
            (current == 0xA) ||
            (current == 0xD) ||
            ((current >= 0x20) && (current <= 0xD7FF)) ||
            ((current >= 0xE000) && (current <= 0xFFFD)) ||
            ((current >= 0x10000) && (current <= 0x10FFFF)))
            out.append(current);
    }
    return out.toString();
}    
Dima
  • 1,045
  • 14
  • 23
8

Whenever invalid xml character comes xml, it gives such error. When u open it in notepad++ it look like VT, SOH,FF like these are invalid xml chars. I m using xml version 1.0 and i validate text data before entering it in database by pattern

Pattern p = Pattern.compile("[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u10000-\u10FFF]+"); 
retunContent = p.matcher(retunContent).replaceAll("");

It will ensure that no invalid special char will enter in xml

SkyWalker
  • 28,384
  • 14
  • 74
  • 132
Komal
  • 200
  • 2
  • 5
  • 1
    The pattern you provide is correct, but does not compile as it is. You need some escaping. The correct is `Pattern.compile("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD\\u10000-\\u10FFF]+")` – k.liakos May 28 '17 at 15:34
6

The character 0x0C is be invalid in XML 1.0 but would be a valid character in XML 1.1. So unless the xml file specifies the version as 1.1 in the prolog it is simply invalid and you should complain to the producer of this file.

Jörn Horstmann
  • 33,639
  • 11
  • 75
  • 118
3

You can filter all 'invalid' chars with a custom FilterReader class:

public class InvalidXmlCharacterFilter extends FilterReader {

    protected InvalidXmlCharacterFilter(Reader in) {
        super(in);
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        int read = super.read(cbuf, off, len);
        if (read == -1) return read;

        for (int i = off; i < off + read; i++) {
            if (!XMLChar.isValid(cbuf[i])) cbuf[i] = '?';
        }
        return read;
    }
}

And run it like this:

InputStream fileStream = new FileInputStream(xmlFile);
Reader reader = new BufferedReader(new InputStreamReader(fileStream, charset));
InvalidXmlCharacterFilter filter = new InvalidXmlCharacterFilter(reader);
InputSource is = new InputSource(filter);
xmlReader.parse(is);
Vadim Zin4uk
  • 1,716
  • 22
  • 18
3

On UTF-8, all the codes on these ranges are not allowed, for XML 1.0:

  • 0..8
  • B..C
  • E..1F
  • D800..DFFF
  • FFFE..FFFF

A regex that can remove then is:

text.replaceAll('[\\x{0}-\\x{8}]|[\\x{B}-\\x{C}]|[\\x{E}-\\x{1F}]|[\\x{D800}-\\x{DFFF}]|[\\x{FFFE}-\\x{FFFF}]', "")

Note: if you are working with XML 1.1, you also need to remove these intervals:

  • 7F..84
  • 86..9F

Refs:

Topera
  • 12,223
  • 15
  • 67
  • 104
1

I just used this project, and found it very handy: https://github.com/rwitzel/streamflyer

Using the InvalidXmlCharacterModifier, as the documentation says.

Like this example:

public String stripNonValidXMLCharacters(final String in) {

  final Modifier modifier = new InvalidXmlCharacterModifier("",
    InvalidXmlCharacterModifier.XML_10_VERSION);

  final ModifyingReader modifyingReader = 
         new ModifyingReader(new StringReader(in), modifier);

  return IOUtils.toString(modifyingReader);
}
  • [Link only answers](https://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers/8259#8259) are considered very low quality and [can get deleted](https://stackoverflow.com/help/deleted-answers), please put the important parts from the linked resource into the answer body. – helvete Jun 27 '23 at 16:39
  • 1
    Hmm... just added an example @helvete – Martin Husted Hartvig Jun 28 '23 at 17:32
0

I faced a similar issue where XML was containing control characters. After looking into the code, I found that a deprecated class,StringBufferInputStream, was used for reading string content.

http://docs.oracle.com/javase/7/docs/api/java/io/StringBufferInputStream.html

This class does not properly convert characters into bytes. As of JDK 1.1, the preferred way to create a stream from a string is via the StringReader class.

I changed it to ByteArrayInputStream and it worked fine.

Mohit
  • 1,740
  • 1
  • 15
  • 19
0

For people who are reading byte array into String and trying to convert to object with JAXB, you can add "iso-8859-1" encoding by creating String from byte array like this:

String JAXBallowedString= new String(byte[] input, "iso-8859-1");

This would replace the conflicting byte to single-byte encoding which JAXB can handle. Obviously this solution is only to parse the xml.

BatBold
  • 21
  • 1
  • 5
0

All of these answers seem to assume that the user is generating the bad XML, rather than receiving it from gSOAP, which should know better!

Jerry Miller
  • 921
  • 1
  • 8
  • 11
0

Today, I've got a similar error:

Servlet.service() for servlet [remoting] in context with path [/***] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: buildDocument failed.] with root cause org.xml.sax.SAXParseException; lineNumber: 19; columnNumber: 91; An invalid XML character (Unicode: 0xc) was found in the value of attribute "text" and element is "label".


After my first encouter with the error, I had re-typed the entire line by hand, so that there was no way for a special character to creep in, and Notepad++ didn't show any non-printable characters (black on white), nevertheless I got the same error over and over.

When I looked up what I've done different than my predecessors, it turned out it was one additional space just before the closing /> (as I've heard was recommended for older parsers, but it shouldn't make any difference anyway, by the XML standards):

<label text="this label's text" layout="cell 0 0, align left" />

When I removed the space:

<label text="this label's text" layout="cell 0 0, align left"/>

everything worked just fine.


So it's definitely a misleading error message.

P. Wolff
  • 21
  • 2