jackson-dataformat-xml icon indicating copy to clipboard operation
jackson-dataformat-xml copied to clipboard

Add possibility to add DOCTYPE element

Open marvin9000 opened this issue 10 years ago • 10 comments

I found no easy way of adding a DTD to the XML output. E.g.:

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

It would be nice to have a feature to add such elements.

marvin9000 avatar Apr 27 '15 09:04 marvin9000

Updating, unfortunately did not have time to implement for 2.7.

cowtowncoder avatar Dec 26 '15 19:12 cowtowncoder

Hi! Did you find a way to solve this?

tudordascalu avatar Apr 29 '20 13:04 tudordascalu

No solution yet. Problem mostly related to how to expose this through API: while XmlMapper can add methods that does not allow per-call config settings unlike ObjectWriter -- and latter is not designed to be extensible. Most format-specific additions are either simple on/off "Feature" settings (which can be toggled via ObjectReader / ObjectWriter) or per-mapper settings.

But I still hope to think of a good mechanism, now that 2.11.0 was released and I plan to spend time on XML module too (there are so many Jackson modules, improvement ideas... and so little time to spent on unpaid hobby).

cowtowncoder avatar Apr 29 '20 23:04 cowtowncoder

Thanks for your reply and I appreciate your effort. I managed to solve my issue by converting the object to string and then appending the DTD.

tudordascalu avatar Apr 30 '20 05:04 tudordascalu

@tudordascalu Ok I am glad you have a work-around, and maybe others can use it in the meantime as well. I will keep this open hoping it can be resolved in a better way eventually.

cowtowncoder avatar Apr 30 '20 06:04 cowtowncoder

Thanks for your reply and I appreciate your effort. I managed to solve my issue by converting the object to string and then appending the DTD.

I try to lookup a way to achieve it, but failed. I'm not sure whether the way meet your expection. For reference, I achieve it in the following way in version 2.11.1.

import java.util.List;

public interface XmlDocument {

  List<String> headers();
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer;
import java.io.IOException;

public class XmlDocumentSerializer extends JsonSerializer<XmlDocument> {

  private final XmlBeanSerializer defaultSerializer;

  public XmlDocumentSerializer(XmlBeanSerializer defaultSerializer) {
    this.defaultSerializer = defaultSerializer;
  }

  @Override
  public void serialize(XmlDocument src, JsonGenerator gen,
      SerializerProvider serializerProvider) throws IOException {
    if (src.headers() == null || src.headers().isEmpty()) {
      defaultSerializer.serialize(src, gen, serializerProvider);
      return;
    }
    boolean pretty = serializerProvider.isEnabled(SerializationFeature.INDENT_OUTPUT);
    StringBuilder builder = new StringBuilder();
    for (String header : src.headers()) {
      builder.append(header);
      if (pretty) {
        builder.append("\n");
      }
    }
    gen.writeRaw(builder.toString());
    defaultSerializer.serialize(src, gen, serializerProvider);
  }
}
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer;

public class XmlDocumentSerializerModifier extends BeanSerializerModifier {
  @Override
  public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
    if (!(serializer instanceof BeanSerializerBase)) {
      return serializer;
    }
    XmlBeanSerializer defaultSerializer = new XmlBeanSerializer((BeanSerializerBase) serializer);
    if (XmlDocument.class.isAssignableFrom(beanDesc.getBeanClass())) {
      return new XmlDocumentSerializer(defaultSerializer);
    }
    return defaultSerializer;
  }
}
public class ExampleXml implements XmlDocument {
  @Override
  public List<String> headers() {
    return Arrays.asList("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>",
        "<!DOCTYPE  SYSTEM \"any\">");
  }
}
public static void main() {
    JacksonXmlModule module = new JacksonXmlModule();
    module.setSerializerModifier(new XmlDocumentSerializerModifier());
    XmlMapper xmlMapper = new XmlMapper(module);
    xmlMapper.writeValueAsString(new ExampleXml());
    // got expected DOCTYPE
}

ljun20160606 avatar Jan 05 '21 06:01 ljun20160606

Been looking for how to do this as well, and arrived here after following the stackoverflow question that was opened over 5 years ago.

Seems like quite the missing feature after all these years. There are use cases where you need or want to append a custom header, either via the serializer or via annotation to the pojo so when it gets processed it uses that.

skyhirider avatar Feb 28 '21 10:02 skyhirider

@skyhirider I'd recommend adding your "vote" on thumbs-up on description: that can help indicate desire by users. Does not solve the issue of there not being enough contributors/time by contributors but helps finding "most-wanted" issues (I add label when it seems something is highly requested).

cowtowncoder avatar Feb 28 '21 23:02 cowtowncoder

@cowtowncoder thanks, just did that. Sorry if the comment was too active, just wanted to highlight the issue. Appreciate all the work that goes into this library just starting to learn and use it.

skyhirider avatar Mar 01 '21 06:03 skyhirider

@skyhirider no problem -- appreciate the interest. And I agree that this would be a good feature to get, was hoping to have time to get it in 2.12 but ended up running out of time (there were tons of other fixes for xml module). Now hoping it could make it in 2.13, as part of mechanism that would solve a few other similar issues (and being flexible enough wrt all aspects of DOCTYPE declaration).

cowtowncoder avatar Mar 01 '21 18:03 cowtowncoder