Java 使用Jackson XmlMapper序列化时添加DTD

Java 使用Jackson XmlMapper序列化时添加DTD,java,xml,serialization,jackson,Java,Xml,Serialization,Jackson,当我序列化我的POJO时,一切都按预期工作。我得到这样的东西: <?xml version='1.0' encoding='UTF-8'?> <gsafeed> ... </gsafeed> <?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE gsafeed PUBLIC "-//Google//DTD GSA Feeds//EN" ""> <gsafeed> ..

当我序列化我的POJO时,一切都按预期工作。我得到这样的东西:

<?xml version='1.0' encoding='UTF-8'?>
<gsafeed>
   ...
</gsafeed>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE gsafeed PUBLIC "-//Google//DTD GSA Feeds//EN" "">
<gsafeed>
   ...
</gsafeed>

...
收件人(Google Search Appliance)似乎希望XML包含如下DTD:

<?xml version='1.0' encoding='UTF-8'?>
<gsafeed>
   ...
</gsafeed>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE gsafeed PUBLIC "-//Google//DTD GSA Feeds//EN" "">
<gsafeed>
   ...
</gsafeed>

...

我怎样才能做到这一点呢?

我通常不喜欢回答“编写自定义序列化程序”的Jackson问题,因为通常有一种更简单、更干净的方法。不幸的是,除了自定义序列化程序之外,我不知道还有什么更好的方法可以实现向序列化输出添加元数据

希望有人能给出一个更简单的解决方案,但这应该能实现您想要实现的目标

创建一个模块以容纳自定义序列化程序
公共类GsaFeedModule扩展了SimpleModule{
私有静态最终字符串NAME=“GsaFeedModule”;
公共GsaFeedModule(){
超级(姓名);
addSerializer(GsaFeed.class,新的GsaFeedSerializer());
}
公共静态类GsaFeedSerializer扩展了JsonSerializer{
@凌驾
public void serialize(GsaFeed GsaFeed,jsonggenerator jsonggenerator,SerializerProvider SerializerProvider)引发IOException{
jsonGenerator.writeRaw(“”);
jsonGenerator.writeRaw(“”);
jsongGenerator.writeStartObject();
//写入字段
jsongGenerator.writeEndObject();
}
}
}
注册模块
XmlMapper xm=newxmlmapper();
registerModule(新的GsaFeedModule());

似乎没有一种优雅的方式来设置DTD。除了实现自定义序列化器之外,还可以考虑在XmlSerializerProvider的初始化后,重写<代码> < <代码> >来编写DTD字符串写入。以下是一个例子:

public class JacksonXmlDTD {
    private static class DtdXmlSerializerProvider extends XmlSerializerProvider {
        private final String dtd;

        public DtdXmlSerializerProvider(
                final XmlSerializerProvider src,
                final SerializationConfig config,
                final SerializerFactory jsf,
                final String dtd) {
            super(src, config, jsf);
            this.dtd = dtd;
        }

        @Override
        protected void _initWithRootName(final ToXmlGenerator xgen, final QName rootName)
                throws IOException {
            super._initWithRootName(xgen, rootName);
            try {
                xgen.getStaxWriter().writeDTD(dtd);
            } catch (final XMLStreamException e) {
                StaxUtil.throwXmlAsIOException(e);
            }
        }

        @Override
        public DefaultSerializerProvider createInstance(
                final SerializationConfig config, final SerializerFactory jsf) {
            return new DtdXmlSerializerProvider(this, config, jsf, dtd);
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        final XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION);
        final String dtd = "<!DOCTYPE gsafeed PUBLIC \"-//Google//DTD GSA Feeds//EN\" \"\">";
        final DtdXmlSerializerProvider serializerProvider = new DtdXmlSerializerProvider(
                (XmlSerializerProvider) xmlMapper.getSerializerProvider(),
                xmlMapper.getSerializationConfig(),
                xmlMapper.getSerializerFactory(),
                dtd);
        xmlMapper.setSerializerProvider(serializerProvider);
        final Map<String, Object> map = new HashMap<>();
        map.put("object", "value");
        System.out.println(xmlMapper.writeValueAsString(map));
    }

}
公共类JacksonXmlDTD{
私有静态类DtdXmlSerializerProvider扩展了XmlSerializerProvider{
私有最终字符串dtd;
公共DtdXmlSerializerProvider(
最终XmlSerializerProvider src,
最终序列化配置,
最终SerializerFactoryJSF,
最终字符串(dtd){
super(src、config、jsf);
this.dtd=dtd;
}
@凌驾
受保护的void\u initWithRootName(最终ToXmlGenerator xgen,最终QName rootName)
抛出IOException{
super.\u initWithRootName(xgen,rootName);
试一试{
xgen.getStaxWriter().writedd(dtd);
}捕获(最终XMLStreamException e){
Statxutil.throwXmlAsIOException(e);
}
}
@凌驾
公共DefaultSerializerProviderCreateInstance(
最终SerializationConfig配置,最终SerializationFactory(jsf){
返回新的DtdXmlSerializerProvider(this、config、jsf、dtd);
}
}
公共静态void main(字符串[]args)引发JsonProcessingException{
final XmlMapper XmlMapper=新的XmlMapper();
enable(ToXmlGenerator.Feature.WRITE_XML_声明);
最终字符串dtd=“”;
最终DtdXmlSerializerProvider serializerProvider=新DtdXmlSerializerProvider(
(XmlSerializerProvider)xmlMapper.getSerializerProvider(),
xmlMapper.getSerializationConfig(),
xmlMapper.getSerializerFactory(),
dtd);
setSerializerProvider(serializerProvider);
final Map=new HashMap();
映射放置(“对象”、“值”);
System.out.println(xmlMapper.writeValueAsString(map));
}
}
输出:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE gsafeed PUBLIC "-//Google//DTD GSA Feeds//EN" ""><HashMap xmlns=""><object>value</object></HashMap>

根据其他答案,不幸的是,没有简单的方法来实现这一点


从长远来看,有一件事可能会有帮助,那就是申请添加这样一个特性——例如,通过特定于XML的
ObjectWriter
公开这一特性听起来很合理。

此解决方案的缺点是它只适用于
GsaFeed
对象,而且还需要定义所有字段(可能会变得冗长)。有点不满意,因为它似乎不是一个外来需求。我按照您的建议提交了一个请求。对我来说,它确实是一个外来需求,从来没有需要过这个,也没有看到过任何关于那个的请求。但我不是说这是一个不合理的功能请求或类似的东西。只是不是基于m的常见需求我的经验太多了。无意冒犯!也许我有一个错误的印象,因为这是我第一次使用它的一次…(输入到谷歌搜索设备,这似乎很挑剔)。因为似乎没有优雅的解决方案,所以我开始准备所需的