【问题标题】:Add DTD when serializing with Jackson XmlMapper使用 Jackson XmlMapper 进行序列化时添加 DTD
【发布时间】:2015-07-02 22:27:09
【问题描述】:

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

<?xml version='1.0' encoding='UTF-8'?>
<gsafeed>
   ...
</gsafeed>

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

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

我怎样才能做到这一点?

【问题讨论】:

  • 因为无论如何似乎都没有优雅的解决方案,所以我将所需的&lt;!DOCTYPE(和&lt;?xml)添加到输出字符串中。

标签: java xml serialization jackson


【解决方案1】:

根据其他答案,不幸的是,没有办法以简单的方式实现这一目标。

可能对长期有用的一件事是提交添加此类功能的请求 - 例如,通过特定于 XML 的 ObjectWriter 公开这听起来像是一个合理的功能。

【讨论】:

  • 有点不满意,因为它似乎不是一个异国情调的要求。我按照您的建议提交了请求。
  • 对我来说,这似乎是一个奇特的要求,从来没有需要这个,也没有看到任何要求。但我并不是说这是一个不合理的功能要求或类似的东西。根据我的经验,这不是一个常见的需求。
  • 无意冒犯!也许我有一个错误的印象,因为这是我第一次使用它...(喂给 Google Search Appliance,这似乎很挑剔)。
【解决方案2】:

似乎没有设置 DTD 的优雅方法。除了实现自定义序列化程序之外,您还可以考虑覆盖 XmlSerializerProvider 以在 XML 生成器初始化后写入 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));
    }

}

输出:

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

【讨论】:

    【解决方案3】:

    我通常鄙视杰克逊问题的“编写自定义序列化程序”的答案,因为通常有一种更简单、更清洁的方法。不幸的是,除了自定义序列化程序之外,我不知道将元数据添加到序列化输出的更好方法。

    希望有人以更简单的解决方案做出回应,但这应该可以实现您想要实现的目标。

    创建一个模块来容纳自定义序列化程序

    public class GsaFeedModule extends SimpleModule {
        private static final String NAME = "GsaFeedModule";
    
        public GsaFeedModule() {
            super(NAME);
            addSerializer(GsaFeed.class, new GsaFeedSerializer());
        }
    
        public static class GsaFeedSerializer extends JsonSerializer<GsaFeed> {
            @Override
            public void serialize(GsaFeed gsaFeed, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                jsonGenerator.writeRaw("<?xml version='1.0' encoding='UTF-8'?>");
                jsonGenerator.writeRaw("<!DOCTYPE gsafeed PUBLIC \"-//Google//DTD GSA Feeds//EN\" \"\">");
                jsonGenerator.writeStartObject();
                // write fields
                jsonGenerator.writeEndObject();
            }
        }
    }
    

    注册模块

    XmlMapper xm = new XmlMapper();
    xm.registerModule(new GsaFeedModule());
    

    【讨论】:

    • 这个解决方案的缺点是它只适用于GsaFeed 对象,并且还需要定义所有字段(可能会变得冗长)。
    猜你喜欢
    • 1970-01-01
    • 2013-06-17
    • 2014-10-22
    • 2016-07-09
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    相关资源
    最近更新 更多