Prevent Jackson XML mapper from adding wstxns to namespaces

后端 未结 2 1197
孤独总比滥情好
孤独总比滥情好 2021-01-16 11:07

When serialising objects to XML and specifying namespaces for properties using @JacksonXmlRootElement(namespace = \"http://...\") Jackson will append or prepen

相关标签:
2条回答
  • 2021-01-16 11:55

    This seems to be the missing piece. It allows you to set the prefix and namespace.

       static class NamespaceXmlFactory extends XmlFactory {
    
        private final String defaultNamespace;
        private final Map<String, String> prefix2Namespace;
    
        public NamespaceXmlFactory(String defaultNamespace, Map<String, String> prefix2Namespace) {
            this.defaultNamespace = Objects.requireNonNull(defaultNamespace);
            this.prefix2Namespace = Objects.requireNonNull(prefix2Namespace);
        }
    
        @Override
        protected XMLStreamWriter _createXmlWriter(IOContext ctxt, Writer w) throws IOException {
            XMLStreamWriter2 writer = (XMLStreamWriter2)super._createXmlWriter(ctxt, w);
            try {
                writer.setDefaultNamespace(defaultNamespace);
                writer.setPrefix("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                for (Map.Entry<String, String> e : prefix2Namespace.entrySet()) {
                    writer.setPrefix(e.getKey(), e.getValue());
                }
            } catch (XMLStreamException e) {
                StaxUtil.throwAsGenerationException(e, null);
            }
            return writer;
        }
    }
    

    The only remaining issue I have is

        @JacksonXmlProperty(localName = "@xsi.type", isAttribute = true, namespace = "http://www.w3.org/2001/XMLSchema-instance")
    @JsonProperty("@xsi.type")
    private String type;
    

    Creates the following output:

    Still trying to resolve how to make it be xsi:type="networkObjectGroupDTO" instead.

    0 讨论(0)
  • 2021-01-16 12:07

    To write XML Jackson uses javax.xml.stream.XMLStreamWriter. You can configure instance of that class and define your own prefixes for namespaces and set default one if needed. To do that we need to extend com.fasterxml.jackson.dataformat.xml.XmlFactory class and override a method which creates XMLStreamWriter instance. Example implementation could look like below:

    class NamespaceXmlFactory extends XmlFactory {
    
        private final String defaultNamespace;
        private final Map<String, String> prefix2Namespace;
    
        public NamespaceXmlFactory(String defaultNamespace, Map<String, String> prefix2Namespace) {
            this.defaultNamespace = Objects.requireNonNull(defaultNamespace);
            this.prefix2Namespace = Objects.requireNonNull(prefix2Namespace);
        }
    
        @Override
        protected XMLStreamWriter _createXmlWriter(IOContext ctxt, Writer w) throws IOException {
            XMLStreamWriter writer = super._createXmlWriter(ctxt, w);
            try {
                writer.setDefaultNamespace(defaultNamespace);
                for (Map.Entry<String, String> e : prefix2Namespace.entrySet()) {
                    writer.setPrefix(e.getKey(), e.getValue());
                }
            } catch (XMLStreamException e) {
                StaxUtil.throwAsGenerationException(e, null);
            }
            return writer;
        }
    }
    

    You can use it as below:

    import com.fasterxml.jackson.core.io.IOContext;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.dataformat.xml.XmlFactory;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
    import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
    import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
    import com.fasterxml.jackson.dataformat.xml.util.StaxUtil;
    
    import javax.xml.stream.XMLStreamException;
    import javax.xml.stream.XMLStreamWriter;
    import java.io.IOException;
    import java.io.Writer;
    import java.util.Collections;
    import java.util.Map;
    import java.util.Objects;
    
    public class XmlMapperApp {
    
        public static void main(String[] args) throws Exception {
            String defaultNamespace = "http://schemas.datacontract.org/2004/07/Vtex.Commerce.WebApps.AdminWcfService.Contracts";
            Map<String, String> otherNamespaces = Collections.singletonMap("a", "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
    
            XmlMapper xmlMapper = new XmlMapper(new NamespaceXmlFactory(defaultNamespace, otherNamespaces));
            xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
    
            System.out.println(xmlMapper.writeValueAsString(new VtexSkuAttributeValues()));
        }
    }
    

    In VtexSkuAttributeValues class you can declare:

    public static final String DEF_NMS = "http://schemas.datacontract.org/2004/07/Vtex.Commerce.WebApps.AdminWcfService.Contracts";
    

    and use it for every class and field where it should be used as default namespace. For example:

    @JacksonXmlProperty(localName = "StockKeepingUnitFieldNameDTO", namespace = DEF_NMS)
    

    For properties, for which you do not want to change name you can use:

    @JacksonXmlProperty(namespace = VtexSkuAttributeValues.DEF_NMS)
    

    Above code prints for some random data:

    <listStockKeepingUnitName>
      <StockKeepingUnitFieldNameDTO xmlns="http://schemas.datacontract.org/2004/07/Vtex.Commerce.WebApps.AdminWcfService.Contracts">
        <fieldName>Name1</fieldName>
        <fieldValues>
          <a:string xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">6184</a:string>
        </fieldValues>
        <idSku>123</idSku>
      </StockKeepingUnitFieldNameDTO>
      <StockKeepingUnitFieldNameDTO xmlns="http://schemas.datacontract.org/2004/07/Vtex.Commerce.WebApps.AdminWcfService.Contracts">
        <fieldName>Name1</fieldName>
        <fieldValues>
          <a:string xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">6184</a:string>
        </fieldValues>
        <idSku>123</idSku>
      </StockKeepingUnitFieldNameDTO>
    </listStockKeepingUnitName>
    

    If it is not what you want you can play with that code and try other methods which are available for you to configure this instance.

    To create this example Jackson in version 2.9.9 was used.

    0 讨论(0)
提交回复
热议问题