如何使用 JAXB 和 spring-boot 将 XML 转换为字符串?

问题描述 投票:0回答:2

当我在包含 pom.xml 文件的文件夹上运行

mvn spring-boot:run
时,应用程序会启动并将 POJO 正确序列化为 XML,但是当我通过转到目标文件夹并使用
java -jar
来启动它时我得到的 jar 文件是由
javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath
引起的。
在我的 Maven 中,我有以下 JAXB 依赖项:

.java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory

这是将 POJO 序列化为 XML 的代码:

<!-- JAXB API --> <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> </dependency> <!-- JAXB Runtime --> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.5</version> <scope>runtime</scope> </dependency>

这是我使用 java -jar 启动应用程序时的日志:

private static final Pattern REMOVE_HEADER = Pattern.compile("\\<\\?xml(.+?)\\?\\>"); public static <T> String toXML(final T data) { try { final var jaxbMarshaller = JAXBContext.newInstance(data.getClass()).createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); final var sw = new StringWriter(); jaxbMarshaller.marshal(data, sw); return XMLUtils.REMOVE_HEADER.matcher(sw.toString()).replaceAll("").strip(); } catch (final JAXBException e) { XMLUtils.LOGGER.error("Error while converting POJO to XML. ERROR: {}.", e.getMessage(), e); } return ""; }

这是生成的带有 JAXB 依赖项的 spring-boot fat jar 的图像:

JAXB dependencies 这是我的依赖树:

2021-12-26 21:19:14,526 [ForkJoinPool.commonPool-worker-11] ERROR com.enterprise.system.shared.util.XMLUtils - Error while converting POJO to XML. ERROR: Implementation of JAXB-API has not been found on module path or classpath.. javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath. - with linked exception: [java.lang.ClassNotFoundException: com.sun.xml.bind.v2.ContextFactory] at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:232) at javax.xml.bind.ContextFinder.find(ContextFinder.java:375) at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:691) at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:632) at com.enterprise.system.shared.util.XMLUtils.toXML(XMLUtils.java:26) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:952) at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:926) at java.base/java.util.stream.AbstractTask.compute(AbstractTask.java:327) at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) Caused by: java.lang.ClassNotFoundException: com.sun.xml.bind.v2.ContextFactory at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:92) at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:125) at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:230) ... 17 more

最后,这是我的模型类:

--- maven-dependency-plugin:3.2.0:tree (default-cli) @ java-eleven-jaxb-hell --- com.enterprise.system:java-eleven-jaxb-hell:jar:0.0.1-SNAPSHOT +- org.slf4j:slf4j-log4j12:jar:1.7.32:compile (optional) | +- org.slf4j:slf4j-api:jar:1.7.32:compile (optional) | \- log4j:log4j:jar:1.2.17:compile (optional) +- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.2:compile | \- org.springframework.boot:spring-boot:jar:2.6.2:compile | +- org.springframework:spring-core:jar:5.3.14:compile | | \- org.springframework:spring-jcl:jar:5.3.14:compile | \- org.springframework:spring-context:jar:5.3.14:compile | +- org.springframework:spring-aop:jar:5.3.14:compile | +- org.springframework:spring-beans:jar:5.3.14:compile | \- org.springframework:spring-expression:jar:5.3.14:compile +- org.projectlombok:lombok:jar:1.18.22:provided +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile | \- jakarta.activation:jakarta.activation-api:jar:1.2.2:compile \- com.sun.xml.bind:jaxb-impl:jar:2.3.5:runtime \- com.sun.activation:jakarta.activation:jar:1.2.2:runtime

我正在使用 java 11,并且我知道在 java 11 中 JAXB 已从 SE JDK 中删除,因为它被视为 EE 功能。

由于 docker 镜像的大小以及使用 docker 容器的安全相关问题,我无法在生产环境中使用

import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @XmlRootElement(name = "finans") @XmlAccessorType(XmlAccessType.FIELD) @Data @NoArgsConstructor @AllArgsConstructor public class FinanTokenDTO { @XmlAttribute private String pln; @XmlAttribute private String ope; @XmlAttribute private String mod; @XmlAttribute private String mis; @XmlAttribute private String val; @XmlAttribute private String car; @XmlAttribute private String dti; @XmlAttribute private String dtf; @XmlAttribute private String ota; @XmlElement private FinanDTO finan; } import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @XmlRootElement(name = "finan") @XmlAccessorType(XmlAccessType.FIELD) @Data @NoArgsConstructor @AllArgsConstructor public class FinanDTO { @XmlAttribute private String prd; @XmlAttribute private String pkg; @XmlAttribute private String val; @XmlAttribute private String fty; }

执行它。

既然 Spring Boot 生成了一个 fat jar,那么应用程序运行时不应该将 

mvn spring-boot:run

应用于 Spring Boot fat jar 生成的文件,就像在 pom.xml 文件夹中使用

java -jar
一样吗?

编辑:

经过大量的挖掘和测试,我发现当我们尝试在 POJO 列表上使用

parallel stream

而不是 stream 来编组多个有效的 POJO 时,就会出现问题。我将代码上传到 java-eleven-jaxb-hell 中以便更好地理解,不幸的是,由于性能问题,我无法将 parallelStream 更改为 stream。请记住,要发生该问题,您必须针对目标文件夹中 spring-boot 生成的 fat jar 运行 mvn spring-boot:run

    

spring spring-boot jaxb java-11
2个回答
0
投票

java -jar



0
投票

<dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>2.3.1</version> </dependency>

一般来说,Sun 已经不存在很多年了,该软件包的存在可能只是为了兼容。今天推荐的 JAXB 实现来自 Glassfish。

© www.soinside.com 2019 - 2024. All rights reserved.