Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse invalid (bad / not well-formed) XML?

Tags:

Currently, I'm working on a feature that involves parsing XML that we receive from another product. I decided to run some tests against some actual customer data, and it looks like the other product is allowing input from users that should be considered invalid. Anyways, I still have to try and figure out a way to parse it. We're using javax.xml.parsers.DocumentBuilder and I'm getting an error on input that looks like the following.

<xml>   ...   <description>Example:Description:<THIS-IS-PART-OF-DESCRIPTION></description>   ... </xml> 

As you can tell, the description has what appears to be an invalid tag inside of it (<THIS-IS-PART-OF-DESCRIPTION>). Now, this description tag is known to be a leaf tag and shouldn't have any nested tags inside of it. Regardless, this is still an issue and yields an exception on DocumentBuilder.parse(...)

I know this is invalid XML, but it's predictably invalid. Any ideas on a way to parse such input?

like image 750
jvhashe Avatar asked Jun 26 '17 17:06

jvhashe


People also ask

What is XML parsing error not well-formed?

"XML Parsing Error" occurs when something is trying to read the XML, not when it is being generated. Also, "not well-formed" usually refers to errors in the structure of the document, such as a missing end-tag, not the characters it contains.

Is it possible to have an XML document that is well-formed but not valid?

Note: An important thing to remember is that when a document is valid it is also "well formed," but a "well formed" document is not necessarily valid. Additionally, you can create XML documents without a DTD, but the XML document can't be considered valid without a document type.

What is a parse error in XML?

XML Parser Error When trying to open an XML document, a parser-error may occur. If the parser encounters an error, it may load an XML document containing the error description. The code example below tries to load an XML document that is not well-formed. You can read more about well-formed XML in XML Syntax.


2 Answers

A standard XML parser will NEVER accept invalid XML, by design.

Your only option is to pre-process the input to remove the "predictably invalid" content, or wrap it in CDATA, prior to parsing it.

like image 42
Jim Garrison Avatar answered Oct 21 '22 19:10

Jim Garrison


That "XML" is worse than invalid – it's not well-formed; see Well Formed vs Valid XML.

An informal assessment of the predictability of the transgressions does not help. That textual data is not XML. No conformant XML tools or libraries can help you process it.

Options, most desirable first:

  1. Have the provider fix the problem on their end. Demand well-formed XML. (Technically the phrase well-formed XML is redundant but may be useful for emphasis.)

  2. Use a tolerant markup parser to cleanup the problem ahead of parsing as XML:

    • Standalone: xmlstarlet has robust recovering and repair capabilities credit: RomanPerekhrest

      xmlstarlet fo -o -R -H -D bad.xml 2>/dev/null 
    • Standalone and C/C++: HTML Tidy works with XML too. Taggle is a port of TagSoup to C++.

    • Python: Beautiful Soup is Python-based. See notes in the Differences between parsers section. See also answers to this question for more suggestions for dealing with not-well-formed markup in Python, including especially lxml's recover=True option. See also this answer for how to use codecs.EncodedFile() to cleanup illegal characters.

    • Java: TagSoup and JSoup focus on HTML. FilterInputStream can be used for preprocessing cleanup.

    • .NET:

      • XmlReaderSettings.CheckCharacters can be disabled to get past illegal XML character problems.
      • @jdweng notes that XmlReaderSettings.ConformanceLevel can be set to ConformanceLevel.Fragment so that XmlReader can read XML Well-Formed Parsed Entities lacking a root element.
      • @jdweng also reports that XmlReader.ReadToFollowing() can sometimes be used to work-around XML syntactical issues, but note rule-breaking warning in #3 below.
      • Microsoft.Language.Xml.XMLParser is said to be “error-tolerant”.
    • PHP: See DOMDocument::$recover and libxml_use_internal_errors(true). See nice example here.

    • Ruby: Nokogiri supports “Gentle Well-Formedness”.

    • R: See htmlTreeParse() for fault-tolerant markup parsing in R.

    • Perl: See XML::Liberal, a "super liberal XML parser that parses broken XML."

  3. Process the data as text manually using a text editor or programmatically using character/string functions. Doing this programmatically can range from tricky to impossible as what appears to be predictable often is not -- rule breaking is rarely bound by rules.

    • For invalid character errors, use regex to remove/replace invalid characters:

      • PHP: preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $s);
      • Ruby: string.tr("^\u{0009}\u{000a}\u{000d}\u{0020}-\u{D7FF}\u{E000‌​}-\u{FFFD}", ' ')
      • JavaScript: inputStr.replace(/[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm, '')
    • For ampersands, use regex to replace matches with &amp;: credit: blhsin, demo

      &(?!(?:#\d+|#x[0-9a-f]+|\w+);) 

Note that the above regular expressions won't take comments or CDATA sections into account.

like image 130
kjhughes Avatar answered Oct 21 '22 20:10

kjhughes