使用 Jackson XmlMapper 序列化时添加 DTD
Add DTD when serializing with Jackson XmlMapper
当我序列化我的 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>
我怎样才能做到这一点?
我通常鄙视 "write a custom serializer" 杰克逊问题的答案,因为通常有更简单、更清晰的方法。不幸的是,除了自定义序列化器之外,我不知道有什么更好的方法可以实现将元数据添加到序列化输出。
希望有人提供更简单的解决方案,但这应该可以实现您想要实现的目标。
创建一个模块来容纳自定义序列化程序
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());
似乎没有设置 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>
根据其他答案,不幸的是没有办法以简单的方式实现这一点。
从长远来看可能有帮助的一件事是提交添加此类功能的请求——例如,通过 XML-特定 ObjectWriter
公开这听起来是一个合理的功能。
当我序列化我的 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>
我怎样才能做到这一点?
我通常鄙视 "write a custom serializer" 杰克逊问题的答案,因为通常有更简单、更清晰的方法。不幸的是,除了自定义序列化器之外,我不知道有什么更好的方法可以实现将元数据添加到序列化输出。
希望有人提供更简单的解决方案,但这应该可以实现您想要实现的目标。
创建一个模块来容纳自定义序列化程序
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());
似乎没有设置 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>
根据其他答案,不幸的是没有办法以简单的方式实现这一点。
从长远来看可能有帮助的一件事是提交添加此类功能的请求——例如,通过 XML-特定 ObjectWriter
公开这听起来是一个合理的功能。