1 package com.fasterxml.jackson.databind.ext; 2 3 import java.io.StringReader; 4 5 import javax.xml.XMLConstants; 6 import javax.xml.parsers.DocumentBuilder; 7 import javax.xml.parsers.DocumentBuilderFactory; 8 import javax.xml.parsers.ParserConfigurationException; 9 10 import org.w3c.dom.Document; 11 import org.w3c.dom.Node; 12 import org.xml.sax.InputSource; 13 14 import com.fasterxml.jackson.databind.DeserializationContext; 15 import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer; 16 17 /** 18 * Base for serializers that allows parsing DOM Documents from JSON Strings. 19 * Nominal type can be either {@link org.w3c.dom.Node} or 20 * {@link org.w3c.dom.Document}. 21 */ 22 public abstract class DOMDeserializer<T> extends FromStringDeserializer<T> 23 { 24 private static final long serialVersionUID = 1L; 25 26 private final static DocumentBuilderFactory DEFAULT_PARSER_FACTORY; 27 static { 28 DocumentBuilderFactory parserFactory = DocumentBuilderFactory.newInstance(); 29 // yup, only cave men do XML without recognizing namespaces... 30 parserFactory.setNamespaceAware(true); 31 // [databind#1279]: make sure external entities NOT expanded by default 32 parserFactory.setExpandEntityReferences(false); 33 // ... and in general, aim for "safety" 34 try { parserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)35 parserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 36 } catch(ParserConfigurationException pce) { 37 // not much point to do anything; could log but... 38 } catch (Error e) { 39 // 14-Jul-2016, tatu: Not sure how or why, but during code coverage runs 40 // (via Cobertura) we get `java.lang.AbstractMethodError` so... ignore that too 41 } 42 43 // [databind#2589] add two more settings just in case 44 try { 45 parserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 46 } catch (Throwable t) { } // as per previous one, nothing much to do 47 try { 48 parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 49 } catch (Throwable t) { } // as per previous one, nothing much to do 50 DEFAULT_PARSER_FACTORY = parserFactory; 51 } 52 DOMDeserializer(Class<T> cls)53 protected DOMDeserializer(Class<T> cls) { super(cls); } 54 55 @Override _deserialize(String value, DeserializationContext ctxt)56 public abstract T _deserialize(String value, DeserializationContext ctxt); 57 parse(String value)58 protected final Document parse(String value) throws IllegalArgumentException { 59 try { 60 return documentBuilder().parse(new InputSource(new StringReader(value))); 61 } catch (Exception e) { 62 throw new IllegalArgumentException("Failed to parse JSON String as XML: "+e.getMessage(), e); 63 } 64 } 65 66 /** 67 * Overridable factory method used to create {@link DocumentBuilder} for parsing 68 * XML as DOM. 69 * 70 * @since 2.7.6 71 */ documentBuilder()72 protected DocumentBuilder documentBuilder() throws ParserConfigurationException { 73 return DEFAULT_PARSER_FACTORY.newDocumentBuilder(); 74 } 75 76 /* 77 /********************************************************** 78 /* Concrete deserializers 79 /********************************************************** 80 */ 81 82 public static class NodeDeserializer extends DOMDeserializer<Node> { 83 private static final long serialVersionUID = 1L; NodeDeserializer()84 public NodeDeserializer() { super(Node.class); } 85 @Override _deserialize(String value, DeserializationContext ctxt)86 public Node _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException { 87 return parse(value); 88 } 89 } 90 91 public static class DocumentDeserializer extends DOMDeserializer<Document> { 92 private static final long serialVersionUID = 1L; DocumentDeserializer()93 public DocumentDeserializer() { super(Document.class); } 94 @Override _deserialize(String value, DeserializationContext ctxt)95 public Document _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException { 96 return parse(value); 97 } 98 } 99 } 100