Заставьте DocumentBuilder.parse игнорировать ссылки DTD

Когда я анализирую свой xml-файл (переменная f) этим методом, я получаю сообщение об ошибке

C:\Documents and Settings\joe\Desktop\aicpcudev\OnlineModule\map.dtd (The system cannot find the path specified)

Я знаю, что у меня нет dtd, и он мне не нужен. Как я могу преобразовать этот объект File в объект Document, игнорируя ошибки ссылок DTD?

private static Document getDoc(File f, String docId) throws Exception{
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(f);


    return doc;
}

Я считаю, что у него есть лучший ответ на этот вопрос.

simgineer 14.12.2017 20:43
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
82
1
77 433
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

I know I do not have the dtd, nor do I need it.

Я с подозрением отношусь к этому заявлению; Ваш документ содержит ссылки на объекты? Если да, то DTD вам определенно понадобится.

В любом случае, обычный способ предотвратить это - использовать каталог XML для определения локального пути для «map.dtd».

вот еще один пользователь, у которого возникла такая же проблема: http://forums.sun.com/thread.jspa?threadID=284209&forumID=34

пользователь ddssot в этом сообщении говорит

myDocumentBuilder.setEntityResolver(new EntityResolver() {
          public InputSource resolveEntity(java.lang.String publicId, java.lang.String systemId)
                 throws SAXException, java.io.IOException
          {
            if (publicId.equals("--myDTDpublicID--"))
              // this deactivates the open office DTD
              return new InputSource(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
            else return null;
          }
});

Далее пользователь упоминает: «Как вы можете видеть, когда синтаксический анализатор попадает в DTD, вызывается преобразователь сущностей. Я узнаю свое DTD с его конкретным идентификатором и возвращаю пустой документ XML вместо реального DTD, останавливая все проверки ...»

Надеюсь это поможет.

Ответ принят как подходящий

Подход, аналогичный предложенному @anjanb

    builder.setEntityResolver(new EntityResolver() {
        @Override
        public InputSource resolveEntity(String publicId, String systemId)
                throws SAXException, IOException {
            if (systemId.contains("foo.dtd")) {
                return new InputSource(new StringReader(""));
            } else {
                return null;
            }
        }
    });

Я обнаружил, что простой возврат пустого InputSource тоже работает?

У меня сработала настройка функций DocumentBuilderFactory. Решение в этом посте не сработало.

Kai Mechel 25.05.2011 18:22

Это также отлично сработало для меня, хотя я думал, что не использую SAX.

devnull69 13.03.2013 18:44

К сожалению, у меня это не сработало. Я все еще получаю ошибку. Хотя @jt сделал это за меня.

Nils-o-mat 26.07.2016 12:10

Спасибо за решение, я думаю, это подход, рекомендованный org.xml. Похоже, материала по этой теме очень много. см. xerces.apache.org/xml-commons/components/resolver/… или en.wikipedia.org/wiki/XML_Catalog и javadoc saxproject.org/apidoc/org/xml/sax/EntityResolver.html и saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html

aliopi 03.08.2017 09:04

Попробуйте установить функции в DocumentBuilderFactory:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

dbf.setValidating(false);
dbf.setNamespaceAware(true);
dbf.setFeature("http://xml.org/sax/features/namespaces", false);
dbf.setFeature("http://xml.org/sax/features/validation", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

DocumentBuilder db = dbf.newDocumentBuilder();
...

В конечном счете, я думаю, что параметры зависят от реализации парсера. Вот некоторая документация для Xerces2, если это поможет.

последний (load-external-dtd) помог мне - спасибо.

Amarghosh 02.12.2009 12:19

Пробуя это, я получил DOMException: NAMESPACE_ERR: Предпринята попытка создать или изменить объект способом, который неверен в отношении пространств имен.. Я исправил это с помощью dbf.setNamespaceAware(true);

Tim Van Laer 05.09.2013 12:03

Просто чтобы вы знали, последняя настройка функции (как указано @Amarghosh) отлично работает с SAXParserFactory.

Alexis Leclerc 20.01.2014 19:55

Мне хватило настройки load-external-dtd.

chris 14.12.2015 11:54

Использование всех вышеперечисленных функций также приводит к сбою кода. Просто использование двух последних функций (не проверяющих) заставляет мой код работать.

Purus 12.07.2016 10:11

Я обнаружил проблему, когда файл DTD находился в файле jar вместе с XML. Я решил проблему на основе приведенных здесь примеров следующим образом: -

DocumentBuilder db = dbf.newDocumentBuilder();
db.setEntityResolver(new EntityResolver() {
    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
        if (systemId.contains("doc.dtd")) {
             InputStream dtdStream = MyClass.class
                     .getResourceAsStream("/my/package/doc.dtd");
             return new InputSource(dtdStream);
         } else {
             return null;
         }
      }
});

Исходный XML (с DTD)

<!DOCTYPE MYSERVICE SYSTEM "./MYSERVICE.DTD">
<MYACCSERVICE>
   <REQ_PAYLOAD>
      <ACCOUNT>1234567890</ACCOUNT>
      <BRANCH>001</BRANCH>
      <CURRENCY>USD</CURRENCY>
      <TRANS_REFERENCE>201611100000777</TRANS_REFERENCE>
   </REQ_PAYLOAD>
</MYACCSERVICE>

Реализация Java DOM для принятия указанного выше XML в виде строки и удаления объявления DTD

public Document removeDTDFromXML(String payload) throws Exception {

    System.out.println("### Payload received in XMlDTDRemover: " + payload);

    Document doc = null;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    try {

        dbf.setValidating(false);
        dbf.setNamespaceAware(true);
        dbf.setFeature("http://xml.org/sax/features/namespaces", false);
        dbf.setFeature("http://xml.org/sax/features/validation", false);
        dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
        dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

        DocumentBuilder db = dbf.newDocumentBuilder();

        InputSource is = new InputSource();
        is.setCharacterStream(new StringReader(payload));
        doc = db.parse(is); 

    } catch (ParserConfigurationException e) {
        System.out.println("Parse Error: " + e.getMessage());
        return null;
    } catch (SAXException e) {
        System.out.println("SAX Error: " + e.getMessage());
        return null;
    } catch (IOException e) {
        System.out.println("IO Error: " + e.getMessage());
        return null;
    }
    return doc;

}

XML-адрес назначения (без DTD)

<MYACCSERVICE>
   <REQ_PAYLOAD>
      <ACCOUNT>1234567890</ACCOUNT>
      <BRANCH>001</BRANCH>
      <CURRENCY>USD</CURRENCY>
      <TRANS_REFERENCE>201611100000777</TRANS_REFERENCE>
   </REQ_PAYLOAD>
</MYACCSERVICE> 

Я работаю с sonarqube, и sonarlint для eclipse показал мне Недоверенный XML следует анализировать без разрешения внешних данных (squid: S2755)

Мне удалось решить это, используя:

    factory = DocumentBuilderFactory.newInstance();

    factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

    // If you can't completely disable DTDs, then at least do the following:
    // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
    // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
    // JDK7+ - http://xml.org/sax/features/external-general-entities
    factory.setFeature("http://xml.org/sax/features/external-general-entities", false);

    // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
    // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
    // JDK7+ - http://xml.org/sax/features/external-parameter-entities
    factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

    // Disable external DTDs as well
    factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

    // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
    factory.setXIncludeAware(false);
    factory.setExpandEntityReferences(false);

Другие вопросы по теме