【问题标题】:Jaxb: xs:attribute null valuesJaxb:xs:属性空值
【发布时间】:2011-05-13 09:32:24
【问题描述】:

注册:Jaxb

我基本上是在尝试在 JAXB 中设置一个角色,即每当遇到空字段时,而不是在输出中忽略它,而是将其设置为空值。

对于 xmlElement,我得到的答案就像我们需要使用 nillable="true" 但我们需要如何设置 null 值。通过谷歌搜索,我发现我们需要使用 use="optional" 但它不适用于我的情况。

我的xsd部分如下:

 <xs:attribute name="RomVersion" type="xs:string" use="required" /> 
 <xs:attribute name="MACAddress" type="xs:string" use="required" /> 
 <xs:attribute name="LargestFreeBlock" type="xs:unsignedInt" use="required" /> 
 <xs:attribute name="TimeSinceLastReset" type="xs:unsignedInt" use="optional" /> 
 <xs:attribute name="ResetReason" type="xs:string" use="optional" /> 
 <xs:attribute name="TimeStamp" type="xs:unsignedInt" use="optional" /> 
 <xs:attribute name="ECOList" type="xs:string" use="optional" /> 
 </xs:complexType>
 </xs:element>

如果有人知道,请尽快给我解决方案。

【问题讨论】:

    标签: jaxb


    【解决方案1】:

    从 XML 架构开始

    previous answer 中,我描述了从 Java 对象开始时如何解决您的用例。根据您对该答案的 cmets,此答案描述了从 XML 模式生成模型时如何完成相同的事情。

    XML 架构(attributeAdapter.xsd)

    对于本示例,我们将使用以下 XML 模式:

    <?xml version="1.0" encoding="utf-8" ?>
    <xs:schema 
        elementFormDefault="qualified"
        targetNamespace="http://www.example.com/adapter" 
        xmlns:nytd="http://www.example.com/adapter" 
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
    
        <xs:element name="root">
            <xs:complexType>
                <xs:attribute name="foo" type="xs:string"/>
                <xs:attribute name="bar" type="xs:string"/>
            </xs:complexType>
        </xs:element>
    
    </xs:schema>
    

    字符串转换器

    我们需要定义一个类来进行特殊的字符串处理。对于这个用例,我们希望空字段/属性值在 XML 文档中被视为空字符串 (""):

    package com.example.adapter;
    
    public class StringConverter {
    
        public static String parseString(String value) {
            if("".equals(value)) {
                return null;
            }
            return value;
        }
    
        public static String printString(String value) {
            if(null == value) {
                return "";
            }
            return value;
        }
    
    }
    

    绑定文件(attributeAdapterBinding.xml)

    我们将需要使用 JAXB 绑定文件来自定义类生成。下面的绑定文件将允许我们利用上面定义的 StringConverter 类:

    <jaxb:bindings 
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
        version="2.1">
        <jaxb:bindings schemaLocation="attributeAdapter.xsd">
            <jaxb:bindings node="//xs:element[@name='root']/xs:complexType">
                <jaxb:bindings node="xs:attribute[@name='foo']">
                    <jaxb:property>
                        <jaxb:baseType>
                            <jaxb:javaType name="java.lang.String"
                                parseMethod="com.example.adapter.StringConverter.parseString"
                                printMethod="com.example.adapter.StringConverter.printString"/>
                        </jaxb:baseType>
                    </jaxb:property>
                </jaxb:bindings>
                <jaxb:bindings node="xs:attribute[@name='bar']">
                    <jaxb:property>
                        <jaxb:baseType>
                            <jaxb:javaType name="java.lang.String"
                                parseMethod="com.example.adapter.StringConverter.parseString"
                                printMethod="com.example.adapter.StringConverter.printString"/>
                        </jaxb:baseType>
                    </jaxb:property>
                </jaxb:bindings>
            </jaxb:bindings>
        </jaxb:bindings>
    </jaxb:bindings>
    

    XJC 通话

    我们将按如下方式进行 XJC 调用:

    xjc -d out -b attributeAdapterBinding.xml attributeAdapter.xsd
    

    域模型(根)

    我们在绑定文件中自定义的字段/属性会被@XmlJavaTypeAdapter注解;

    package com.example.adapter;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.XmlType;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "")
    @XmlRootElement(name = "root")
    public class Root {
    
        @XmlAttribute
        @XmlJavaTypeAdapter(Adapter1 .class)
        protected String foo;
    
        @XmlAttribute
        @XmlJavaTypeAdapter(Adapter2 .class)
        protected String bar;
    
        public String getFoo() {
            return foo;
        }
    
        public void setFoo(String value) {
            this.foo = value;
        }
    
        public String getBar() {
            return bar;
        }
    
        public void setBar(String value) {
            this.bar = value;
        }
    
    }
    

    XmlAdapter (Adapter1)

    生成的 XmlAdapter 类将如下所示。注意它是如何利用我们的 StringConverter 类的:

    package com.example.adapter;
    
    import javax.xml.bind.annotation.adapters.XmlAdapter;
    
    public class Adapter1 extends XmlAdapter<String, String> {
    
        public String unmarshal(String value) {
            return (com.example.adapter.StringConverter.parseString(value));
        }
    
        public String marshal(String value) {
            return (com.example.adapter.StringConverter.printString(value));
        }
    
    }
    

    演示

    现在如果我们运行以下演示代码:

    package com.example.adapter;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            Root root = new Root();
            root.setFoo(null);
            root.setBar(null);
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(root, System.out);
        }
    
    }
    

    输出

    我们会得到想要的输出:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <root xmlns="http://www.example.com/adapter" foo="" bar=""/>
    

    更新(备用绑定文件)

    或者,如果您希望适配器应用于xsd:string 类型的所有属性,那么您可以使用类似于以下内容的绑定文件;

    <jaxb:bindings 
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
        version="2.1">
        <jaxb:globalBindings>
            <jaxb:javaType 
                name="String"
                xmlType="xs:string"
                parseMethod="com.example.adapter.StringConverter.parseString"
                printMethod="com.example.adapter.StringConverter.printString"/>
        </jaxb:globalBindings>
    
    </jaxb:bindings>
    

    【讨论】:

    • 它工作正常我使用 globalBindings 因为我的 xsd 非常大,所以创建 binding.xml 将是一个忙碌的过程。但是如果我想将相同的东西应用于 long , int 等不仅是字符串,我可以在 globalBindings 中以与字符串相同的方式定义它吗?
    • @Rekha - 您应该能够对任何简单类型使用相同的策略。
    【解决方案2】:

    从 Java 对象开始

    对于映射为 @XmlAttribute 的字段/属性,JAXB 实现(Metro、MOXy、JaxMe 等)会将空字符串 ("") 值编组为 property=""。您可以使用XmlAdapter 将您的空值公开为空字符串以获得所需的行为:

    NullStringAdapter

    import javax.xml.bind.annotation.adapters.XmlAdapter;
    
    public class NullStringAdapter extends XmlAdapter<String, String> {
    
        @Override
        public String unmarshal(String v) throws Exception {
            if("".equals(v)) {
                return null;
            }
            return v;
        }
    
        @Override
        public String marshal(String v) throws Exception {
            if(null == v) {
                return "";
            }
            return v;
        }
    
    }
    

    以下是您在域模型中指定适配器的方式。同一个适配器可用于许多属性:

    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    
    @XmlRootElement
    public class Root {
    
        private String foo;
        private String bar;
    
        @XmlAttribute
        @XmlJavaTypeAdapter(NullStringAdapter.class)
        public String getFoo() {
            return foo;
        }
    
        public void setFoo(String foo) {
            this.foo = foo;
        }
    
        @XmlAttribute
        @XmlJavaTypeAdapter(NullStringAdapter.class)
        public String getBar() {
            return bar;
        }
    
        public void setBar(String bar) {
            this.bar = bar;
        }
    
    }
    

    演示

    您可以通过运行以下演示代码来演示该概念:

    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            Root root = new Root();
            root.setFoo(null);
            root.setBar(null);
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(root, System.out);
        }
    
    }
    

    输出

    以下是演示代码输出:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <root bar="" foo=""/>
    

    有关 JAXB 的 XmlAdapter 的更多信息,请参阅:

    【讨论】:

    • 但是类文件是使用 xjc 命令生成的。我们不应该对此进行任何更改。我指的是更改:@XmlJavaTypeAdapter(NullStringAdapter.class)
    • @Rekha - 我添加了第二个答案,描述了从 XML 模式开始时如何处理此用例:stackoverflow.com/questions/5989922/…
    • 现在客户希望出现默认值而不是空白字符串。喜欢: ... 然后假设这个 XML 片段: <... first="31" third="42" ..> 所需的 xml 应该是:<... first="31" second="2" third="42" ...>
    • @Rekha - 你能为这个新问题提出一个新问题吗?
    【解决方案3】:

    您可以为此使用默认值插件。

    请看那个问题:JAXB xjc: How to generate code for Strings that returns empty if the value is null?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-26
      • 2013-01-14
      • 2013-11-12
      • 2017-04-25
      • 1970-01-01
      • 2012-02-18
      相关资源
      最近更新 更多