如何通过JAXB生成带单引号的XmlAttribute属性值
解决JAXB生成XML时属性使用单引号的问题
我来帮你搞定这个JAXB生成XML时属性用单引号的问题~首先得明确:JAXB默认会用双引号包裹属性值,这是符合XML规范的,但如果需求强制要求单引号,我们可以通过两种方式实现:
方法一:自定义XMLStreamWriter(标准JAXB通用)
标准JAXB的Marshaller本身没有直接设置引号类型的参数,我们可以通过包装XMLStreamWriter,重写属性写入逻辑来替换引号:
1. 实现自定义SingleQuoteXMLStreamWriter
这个类会代理原有的XMLStreamWriter,在写入属性时把双引号替换成单引号:
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; public class SingleQuoteXMLStreamWriter implements XMLStreamWriter { private final XMLStreamWriter delegate; public SingleQuoteXMLStreamWriter(XMLStreamWriter delegate) { this.delegate = delegate; } // 核心:重写属性写入方法,替换引号 @Override public void writeAttribute(String localName, String value) throws XMLStreamException { delegate.writeAttribute(localName, value.replace("\"", "'")); } @Override public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException { delegate.writeAttribute(namespaceURI, localName, value.replace("\"", "'")); } @Override public void writeAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException { delegate.writeAttribute(prefix, namespaceURI, localName, value.replace("\"", "'")); } // 以下是所有其他方法的代理实现,直接调用delegate对应方法即可 @Override public void writeStartDocument() throws XMLStreamException { delegate.writeStartDocument(); } @Override public void writeStartDocument(String version) throws XMLStreamException { delegate.writeStartDocument(version); } @Override public void writeStartDocument(String encoding, String version) throws XMLStreamException { delegate.writeStartDocument(encoding, version); } @Override public void writeEndDocument() throws XMLStreamException { delegate.writeEndDocument(); } @Override public void writeStartElement(String localName) throws XMLStreamException { delegate.writeStartElement(localName); } @Override public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException { delegate.writeStartElement(namespaceURI, localName); } @Override public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { delegate.writeStartElement(prefix, localName, namespaceURI); } @Override public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException { delegate.writeEmptyElement(namespaceURI, localName); } @Override public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { delegate.writeEmptyElement(prefix, localName, namespaceURI); } @Override public void writeEmptyElement(String localName) throws XMLStreamException { delegate.writeEmptyElement(localName); } @Override public void writeEndElement() throws XMLStreamException { delegate.writeEndElement(); } @Override public void writeEndPrefixMapping(String prefix) throws XMLStreamException { delegate.writeEndPrefixMapping(prefix); } @Override public void writeCharacters(String text) throws XMLStreamException { delegate.writeCharacters(text); } @Override public void writeCharacters(char[] text, int start, int len) throws XMLStreamException { delegate.writeCharacters(text, start, len); } @Override public void writeRaw(String data) throws XMLStreamException { delegate.writeRaw(data); } @Override public void writeRaw(char[] data, int start, int len) throws XMLStreamException { delegate.writeRaw(data, start, len); } @Override public void writeProcessingInstruction(String target) throws XMLStreamException { delegate.writeProcessingInstruction(target); } @Override public void writeProcessingInstruction(String target, String data) throws XMLStreamException { delegate.writeProcessingInstruction(target, data); } @Override public void writeComment(String data) throws XMLStreamException { delegate.writeComment(data); } @Override public void writeCData(String data) throws XMLStreamException { delegate.writeCData(data); } @Override public void writeDTD(String dtd) throws XMLStreamException { delegate.writeDTD(dtd); } @Override public void writeEntityRef(String name) throws XMLStreamException { delegate.writeEntityRef(name); } @Override public void writeStartPrefixMapping(String prefix, String namespaceURI) throws XMLStreamException { delegate.writeStartPrefixMapping(prefix, namespaceURI); } @Override public void flush() throws XMLStreamException { delegate.flush(); } @Override public void close() throws XMLStreamException { delegate.close(); } @Override public String getPrefix(String namespaceURI) throws XMLStreamException { return delegate.getPrefix(namespaceURI); } @Override public void setPrefix(String prefix, String namespaceURI) throws XMLStreamException { delegate.setPrefix(prefix, namespaceURI); } @Override public void setDefaultNamespace(String namespaceURI) throws XMLStreamException { delegate.setDefaultNamespace(namespaceURI); } @Override public void setNamespaceContext(javax.xml.namespace.NamespaceContext context) throws XMLStreamException { delegate.setNamespaceContext(context); } @Override public javax.xml.namespace.NamespaceContext getNamespaceContext() { return delegate.getNamespaceContext(); } @Override public Object getProperty(String name) throws IllegalArgumentException { return delegate.getProperty(name); } }
2. 使用自定义Writer生成XML
在你的业务代码中,用这个自定义Writer来包装Marshaller的输出:
import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamWriter; import java.io.StringWriter; public class JaxbDemo { public static void main(String[] args) throws Exception { // 初始化你的Entity实例 Entity entity = new Entity(); entity.setX("test-instance"); // 创建JAXB上下文和Marshaller JAXBContext jaxbContext = JAXBContext.newInstance(Entity.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 使用自定义Writer输出 StringWriter stringWriter = new StringWriter(); XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); XMLStreamWriter originalWriter = xmlOutputFactory.createXMLStreamWriter(stringWriter); SingleQuoteXMLStreamWriter singleQuoteWriter = new SingleQuoteXMLStreamWriter(originalWriter); marshaller.marshal(entity, singleQuoteWriter); singleQuoteWriter.flush(); singleQuoteWriter.close(); // 打印结果 System.out.println(stringWriter.toString()); } } // 你的Entity类保持原有标注即可 import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Entity") public class Entity { private String x; @XmlAttribute(name = "Type") public String getX() { return x; } public void setX(String x) { this.x = x; } }
方法二:使用EclipseLink MOXy(更简洁)
如果你可以切换到MOXy作为JAXB实现,它提供了直接设置属性引号的配置,无需自定义Writer:
1. 引入MOXy依赖(Maven示例)
<dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.moxy</artifactId> <version>4.0.2</version> </dependency>
2. 配置Marshaller属性
只需要给Marshaller设置ATTRIBUTE_QUOTE_CHARACTER属性为单引号即可:
import org.eclipse.persistence.jaxb.MarshallerProperties; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; public class MoxyDemo { public static void main(String[] args) throws Exception { Entity entity = new Entity(); entity.setX("test-instance"); JAXBContext jaxbContext = JAXBContext.newInstance(Entity.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 关键:设置属性引号为单引号 marshaller.setProperty(MarshallerProperties.ATTRIBUTE_QUOTE_CHARACTER, '\''); marshaller.marshal(entity, System.out); } }
为什么你之前的尝试失败?
- 把
@XmlAttribute(name="Type='test-instance'")这种写法是错误的:name参数只用来指定属性的名称,不是整个键值对,所以会生成Type='test-instance'=""这种错误的属性。 - JAXB中未初始化的
@XmlAttribute默认不会输出,而且@XmlAttribute确实没有required属性(只有@XmlElement有),所以强制输出未初始化属性的思路走不通。
内容的提问来源于stack exchange,提问作者Mr.Honda
相关产品推荐
相关产品推荐

