在Java EE应用中常遇到针对xml文件的处理,而针对这种处理有JDK自带的类,开源组件自带的实现类。 由于从JDK1.8移植到JDK11以上 JDK默认不带一些类,从weblogic、websphere、tomcat移植到TongWeb上,默认的xml解析类不同导致一些解析问题。

       如下把一些常用的xml解析类列出来,供大家参考,当出现xml解析问题时,要根据实际应用调整。

                                                                       xml解析参数

#JDK1.8默认实现类

-Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

#开源xerces.jar实现类

-Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl

#JDK1.8默认实现类

-Djavax.xml.stream.XMLInputFactory=com.sun.xml.internal.stream.XMLInputFactoryImpl

#开源实现类

-Djavax.xml.stream.XMLInputFactory=com.ctc.wstx.stax.WstxInputFactory

-Djavax.xml.stream.XMLInputFactory=com.bea.xml.stream.MXParserFactory 

#JDK1.8默认实现类

-Djavax.xml.stream.XMLOutputFactory=com.sun.xml.internal.stream.XMLOutputFactoryImpl

#开源实现类

-Djavax.xml.stream.XMLOutputFactory=com.ctc.wstx.stax.WstxOutputFactory

#JDK1.8默认实现类

-Djavax.xml.bind.JAXBContext=com.sun.xml.internal.bind.v2.ContextFactory

#开源实现类

-Djavax.xml.bind.JAXBContext=com.sun.xml.bind.v2.ContextFactory

#weblogic默认实现类。 xml的双引号会转义成

-Djavax.xml.bind.JAXBContext=org.eclipse.persistence.jaxb.JAXBContextFactory

#jakarta命名空间的

-Djakarta.xml.bind.JAXBContext=org.glassfish.jaxb.runtime.v2.ContextFactory

#JDK1.8默认实现类

-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl

#开源实现类

-Djavax.xml.parsers.SAXParserFactory=com.ctc.wstx.sax.WstxSAXParserFactory

#JDK1.8默认实现类

-Djavax.xml.datatype.DatatypeFactory=com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl

#开源实现类

-Djavax.xml.datatype.DatatypeFactory=org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl

#开源实现类

-Djavax.xml.soap.SAAJMetaFactory=com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl

#JDK1.8默认值

-Djavax.xml.soap.SAAJMetaFactory=com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl

通常xml解析实现类的优先级由高到低依次为:

1. 以-D参数指定的类为最优先。

2. 其次,若无-D参数,则以jar文件META-INF/services目录下设置的为准,如图。(META-INF/services 文件夹是 Java 的「自动服务发现」目录,用来实现 SPI(Service Provider Interface)机制)

3. 若无以上配置,则以代码中指定为准。

案例1:

      应用在TongWeb下报错如下:

Caused by: jakarta.xml.bind.JAXBException:  
Exception Description: Invalid XmlElementRef on property rPrOrAliasOrLock on class org.docx4j.wml.SdtPr. Referenced Element not declared. 
	at com.tongweb.persistence.jaxb.JAXBContext$TypeMappingInfoInput.createContextState(JAXBContext.java:1151) 
	at com.tongweb.persistence.jaxb.JAXBContext.<init>(JAXBContext.java:186) 
	at com.tongweb.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:153) 
	at com.tongweb.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:140) 
	at com.tongweb.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:100) 
	at com.tongweb.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:90) 
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) 
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) 
	at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:262) 
	at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:249) 
	at jakarta.xml.bind.ContextFinder.find(ContextFinder.java:456) 
	at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:656) 
	at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:599) 
	at com.glodon.ureport.customize.WordDefaultStyle.setTheme4NoItalic(WordDefaultStyle.java:59) 

 可以看出的问题是:

1. 是 jakarta.xml.bind.JAXBContext,而不是 javax.xml.bind.JAXBContext。

2. 实现类找到了TongWeb自带的 com.tongweb.persistence.jaxb.JAXBContextFactory 导致解析xml文件出错。因为tongweb-jaxb.jar/META-INF/services目录下有这个配置,所以优先加载了。

3. 应用采用JDK17,而JDK17中没有com.sun.xml.internal.bind.v2.ContextFactory这个类了,所以要从应用中找实现类。

由此可以判断应用中带有javax.xml.bind.JAXBContext的实现类,查找jaxb相关jar,可以找到相关配置,用的org.glassfish.jaxb.runtime.v2.ContextFactory

#第三方开源
-Djakarta.xml.bind.JAXBContext=com.sun.xml.bind.v2.ContextFactory 
-Djakarta.xml.bind.JAXBContext=org.glassfish.jaxb.runtime.v2.ContextFactory
#JDK自带,JDK11后不带
-Djakarta.xml.bind.JAXBContext=com.sun.xml.internal.bind.v2.ContextFactory

案例2:

      hibernate处理validation.xml报错如下:

Caused by: jakarta.validation.ValidationException: HV000100: Unable to parse META-INF/validation.xml.
	at org.hibernate.validator.internal.xml.config.ValidationXmlParser.parseValidationXml(ValidationXmlParser.java:102)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getBootstrapConfiguration(AbstractConfigurationImpl.java:514)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.parseValidationXml(AbstractConfigurationImpl.java:709)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.buildValidatorFactory(AbstractConfigurationImpl.java:423)
	at jakarta.validation.Validation.buildDefaultValidatorFactory(Validation.java:103)
	at org.hibernate.boot.beanvalidation.TypeSafeActivator.getValidatorFactory(TypeSafeActivator.java:511)
	... 88 more
Caused by: org.xml.sax.SAXParseException: cvc-elt.1.a: Cannot find the declaration of element 'validation-config'.
	at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204)
	at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:135)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
	at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:2133)
	at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:830)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:376)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:615)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3079)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:836)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:114)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:542)
	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:889)
	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:825)
	at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.StreamValidatorHelper.validate(StreamValidatorHelper.java:178)
	at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorImpl.validate(ValidatorImpl.java:115)
	at java.xml/javax.xml.validation.Validator.validate(Validator.java:124)
	at org.hibernate.validator.internal.xml.config.ValidationXmlParser.parseValidationXml(ValidationXmlParser.java:88)
	... 93 more
	

可以调整的参数如下:

# 应用中有xercesImpl.jar
-Djavax.xml.validation.SchemaFactory=org.apache.xerces.jaxp.validation.ValidatorImpl
#JDK默认实现类
-Djavax.xml.validation.SchemaFactory=com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorImpl

      重点说明:有时因为应用jar包过多,不确定默认值采用的哪一个,可以参考如下servlet代码在应用中运行遍历类路径下services目录下的默认值和-D值。

package com.tong;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


@WebServlet("/scan-services")
public class ServicesScannerServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();

        out.println("<html>");
        out.println("<head><meta charset=UTF-8></head>");
        out.println("<body style='padding:20px;font-family:Arial'>");
        out.println("<h2>所有 Jar 中 META-INF/services/ 下的全部文件</h2><hr>");

        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> urls = loader.getResources("META-INF/services/");
        int index = 1;

        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            String protocol = url.getProtocol();

            if ("jar".equals(protocol)) {
                // ===== 处理 Jar 文件 =====
                String jarPath = ((JarURLConnection) url.openConnection()).getJarFile().getName();
                String jarName = new File(jarPath).getName();

                try (JarFile jarFile = new JarFile(jarPath)) {
                    Enumeration<JarEntry> entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        String name = entry.getName();

                        // 只匹配 META-INF/services/ 下的文件(不是目录)
                        if (name.startsWith("META-INF/services/") && !entry.isDirectory()) {
                            String fileName = name.substring("META-INF/services/".length());

                            out.println("<div style='border:1px solid #ccc;margin:10px 0;padding:12px;'>");
                            out.println("<div><b>" + (index++) + "、</b></div>");
                            out.println("<div>Jar 包:" + jarPath + "</div>");
                            out.println("<div>文件:" + fileName + "   ,对应-D参数检查:"+System.getProperty(fileName)+"</div>");
                            out.println("<div>内容:</div>");
                            out.println("<pre style='background:#f8f8f8;padding:8px;'>");

                            // 读取内容
                            try (InputStream is = jarFile.getInputStream(entry);
                                 BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {

                                String line;
                                int lineNum = 1;
                                while ((line = br.readLine()) != null) {
                                    line = line.trim();
                                    if (line.isEmpty() || line.startsWith("#")) continue;
                                    out.println(lineNum++ + ": " + line);
                                }
                            }
                            out.println("</pre>");
                            out.println("</div>");
                        }
                    }
                }
            }
        }

        out.println("<hr>扫描完成");
        out.println("</body></html>");
    }
}

输出如下:

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐