You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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;
    }
}

如果你可以切换到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:34:01