Java JAXB编组:将空对象视为null

Java JAXB编组:将空对象视为null,java,jaxb,moxy,Java,Jaxb,Moxy,我想用一个简单的例子来解释我的问题: 傅: MyClass: 示例对象: 当我现在尝试封送myObj时,它会抛出如下异常: org.eclipse.persistence.oxm.record.ValidatingMarshalRecord$MarshalSAXParseException; cvc-complex-type.2.4.b: The content of element 'n0:anyFooObj ' is not complete. One of '{"AnyNamespace

我想用一个简单的例子来解释我的问题:

傅:

MyClass:

示例对象:

当我现在尝试封送myObj时,它会抛出如下异常:

org.eclipse.persistence.oxm.record.ValidatingMarshalRecord$MarshalSAXParseException; 
cvc-complex-type.2.4.b: The content of element 'n0:anyFooObj ' is not complete.
One of '{"AnyNamespace":someBarObj}' is expected.
这是因为anyFooObj已初始化,但它是必需的 有些巴罗布不是

可能的解决办法:

我知道我可以将此方法添加到MyClass:

但是我有很多类,这些类有很多非必填字段。 所以这个解决方案需要很长时间,看起来也不是一个合适的解决方案

我的问题:

有没有办法告诉JAXB它应该像对待空对象一样对待空对象?或者它应该忽略未正确设置的元素。比如说:

@XmlElement(required = false, ingnoreWhenNotMarshallable = true)
Foo anyFooObj;
注:


我不是代码的开发者。我只需要将JAXB添加到项目中,并使所有内容都与给定的XSD文件兼容。我不允许更改类之间的关系。

我认为您试图让JAXB marshaller做一些它实际上不是设计用来做的事情,所以我认为您进入了黑客领域。我建议将需求往后推,以尽量避免出现这个问题

这就是说,如果您必须这样做,那么根据您的要求,避免为每个类/字段编写代码,我认为您应该使用反射来实现这一点——我在下面提供了一个示例,该示例反射地检查所有字段的值

有用的扩展将是:

有没有考虑过吸气剂的方法? 通过要求该字段有一个附加注释,使空设置行为选择加入-您可以将其命名为@JAXBNullIfEmpty Example.java:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringWriter;
import java.lang.reflect.Field;

public class Example
{
    public abstract static class JAXBAutoNullifierForEmptyOptionalFields
    {
        void beforeMarshal(Marshaller x)
        {
            try
            {
                for (Field field : this.getClass().getFields())
                {
                    final XmlElement el = field.getAnnotation(XmlElement.class);

                    // If this is an optional field, it has a value & it has no fields populated then we should replace it with null
                    if (!el.required())
                    {
                        if (JAXBAutoNullifierForEmptyOptionalFields.class.isAssignableFrom(field.getType()))
                        {
                            final JAXBAutoNullifierForEmptyOptionalFields val = (JAXBAutoNullifierForEmptyOptionalFields) field.get(
                                    this);

                            if (val != null && !val.hasAnyElementFieldsPopulated())
                                field.set(this, null); // No fields populated, replace with null
                        }
                    }
                }
            }
            catch (IllegalAccessException e)
            {
                throw new RuntimeException("Error determining if class has all required fields: " + this, e);
            }
        }


        boolean hasAnyElementFieldsPopulated()
        {
            for (Field field : this.getClass().getFields())
            {
                try
                {
                    if (field.isAnnotationPresent(XmlElement.class))
                    {
                        // Retrieve value
                        final Object val = field.get(this);

                        // If the value is non-null then at least one field has been populated
                        if (val != null)
                        {
                            return true;
                        }
                    }
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException("Error determining if class has any populated JAXB fields: " + this, e);
                }
            }

            // There were no fields with a non-null value
            return false;
        }
    }

    @XmlRootElement
    public static class MyJAXBType extends JAXBAutoNullifierForEmptyOptionalFields
    {
        @XmlElement
        public String someField;

        @XmlElement
        public MyJAXBType someOtherField;


        public MyJAXBType()
        {
        }


        public MyJAXBType(final String someField, MyJAXBType someOtherField)
        {
            this.someField = someField;
            this.someOtherField = someOtherField;
        }
    }


    public static void main(String[] args) throws Exception
    {
        final Marshaller marshaller = JAXBContext.newInstance(MyJAXBType.class).createMarshaller();

        MyJAXBType innerValue = new MyJAXBType(); // Unpopulated inner value
        MyJAXBType value = new MyJAXBType("some text value", innerValue);

        final StringWriter sw = new StringWriter();

        marshaller.marshal(value, sw); // Omits "someOtherField"

        System.out.println(sw.toString());
    }
}

好的,someBarObj确实将XmlElement注释属性required设置为true。不可能让存在的对象看起来好像不存在一样。注释属性应设置为false或省略。
org.eclipse.persistence.oxm.record.ValidatingMarshalRecord$MarshalSAXParseException; 
cvc-complex-type.2.4.b: The content of element 'n0:anyFooObj ' is not complete.
One of '{"AnyNamespace":someBarObj}' is expected.
void beforeMarshal(Marshaller m){
    if(! anyFooObj.chosen)
        anyFooObj= null;
    }
}
@XmlElement(required = false, ingnoreWhenNotMarshallable = true)
Foo anyFooObj;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringWriter;
import java.lang.reflect.Field;

public class Example
{
    public abstract static class JAXBAutoNullifierForEmptyOptionalFields
    {
        void beforeMarshal(Marshaller x)
        {
            try
            {
                for (Field field : this.getClass().getFields())
                {
                    final XmlElement el = field.getAnnotation(XmlElement.class);

                    // If this is an optional field, it has a value & it has no fields populated then we should replace it with null
                    if (!el.required())
                    {
                        if (JAXBAutoNullifierForEmptyOptionalFields.class.isAssignableFrom(field.getType()))
                        {
                            final JAXBAutoNullifierForEmptyOptionalFields val = (JAXBAutoNullifierForEmptyOptionalFields) field.get(
                                    this);

                            if (val != null && !val.hasAnyElementFieldsPopulated())
                                field.set(this, null); // No fields populated, replace with null
                        }
                    }
                }
            }
            catch (IllegalAccessException e)
            {
                throw new RuntimeException("Error determining if class has all required fields: " + this, e);
            }
        }


        boolean hasAnyElementFieldsPopulated()
        {
            for (Field field : this.getClass().getFields())
            {
                try
                {
                    if (field.isAnnotationPresent(XmlElement.class))
                    {
                        // Retrieve value
                        final Object val = field.get(this);

                        // If the value is non-null then at least one field has been populated
                        if (val != null)
                        {
                            return true;
                        }
                    }
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException("Error determining if class has any populated JAXB fields: " + this, e);
                }
            }

            // There were no fields with a non-null value
            return false;
        }
    }

    @XmlRootElement
    public static class MyJAXBType extends JAXBAutoNullifierForEmptyOptionalFields
    {
        @XmlElement
        public String someField;

        @XmlElement
        public MyJAXBType someOtherField;


        public MyJAXBType()
        {
        }


        public MyJAXBType(final String someField, MyJAXBType someOtherField)
        {
            this.someField = someField;
            this.someOtherField = someOtherField;
        }
    }


    public static void main(String[] args) throws Exception
    {
        final Marshaller marshaller = JAXBContext.newInstance(MyJAXBType.class).createMarshaller();

        MyJAXBType innerValue = new MyJAXBType(); // Unpopulated inner value
        MyJAXBType value = new MyJAXBType("some text value", innerValue);

        final StringWriter sw = new StringWriter();

        marshaller.marshal(value, sw); // Omits "someOtherField"

        System.out.println(sw.toString());
    }
}