java - JAXB 中的非法 XML/异常取消/编码影子变量

标签 java xml jaxb

我有以下问题:我有一些代码(我无法更改),其中父类的一个变量被子类的变量遮蔽。当注释为 @XmlAttribute 并使用 JAXB 编码时,会导致非法 XML,而在解码时会导致异常(由于非法 XML)。这是一个显示问题的最小示例:

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.PrintStream;

    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    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.helpers.DefaultValidationEventHandler;

    import org.junit.Test;

    public class InheritanceJaxbTest {

        @Test
        public void testInheritanceField() {
            B b = new B("value");
            String xml = toXML(b);
            System.out.println(xml);
            B b_out = fromXML(xml);
            System.out.println(b_out.myField);
        }

        @XmlRootElement
        @XmlAccessorType(XmlAccessType.FIELD)
        private static class A {

            public A() {
            }

            public A(String myField) {
                this.myField = myField;
            }

            @XmlAttribute
            private String myField;
        }

        @XmlRootElement
        @XmlAccessorType(XmlAccessType.FIELD)
        private static class B extends A {

            public B() {
                super();
            }

            public B(String myField) {
                super(myField);
                this.myField = myField;
            }

            @XmlAttribute
            private String myField;
        }

        public <T> T fromXML(String xml) {
            try {
                JAXBContext jc = JAXBContext.newInstance(A.class, B.class);
                Unmarshaller unmarshaller = jc.createUnmarshaller();
                unmarshaller.setEventHandler(new DefaultValidationEventHandler());
                return (T) unmarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes()));
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }

        public String toXML(Object obj) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                PrintStream ps = new PrintStream(baos);
                JAXBContext jc = JAXBContext.newInstance(A.class, B.class);
                Marshaller marshaller = jc.createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
                marshaller.setEventHandler(new DefaultValidationEventHandler());
                marshaller.marshal(obj, ps);
                return baos.toString();
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }
    }

这会产生以下(明显非法的)XML:

<b myField="value" myField="value"/>

后续的解码操作会抛出以下异常:

java.lang.RuntimeException: javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException: Attribute "myField" was already specified for element "b".]
at InheritanceJaxbTest.fromXML(InheritanceJaxbTest.java:67)

由于我基本上无法更改底层Java类,因此我想使用某种XmlAdapter或自定义XmlStreamWriter或类似的东西来解决这个问题。关于如何继续的任何建议?

This question相关,但仍然没有给出如何在不更改 Java 类的情况下继续进行的见解。

最佳答案

我会通过编写自定义 annotation reader 来解决这个问题。注释阅读器基本上是读取类注释的东西。因此,您可以实现自己的阅读器,根据需要处理阴影字段的情况。

public interface AnnotationReader<T,C,F,M> {

// ...

    /**
     * Reads an annotation on a property that consists of a field.
     */
    <A extends Annotation> A getFieldAnnotation(Class<A> annotation,
                                                F field, Locatable srcpos);

    /**
     * Checks if the given field has an annotation.
     */
    boolean hasFieldAnnotation(Class<? extends Annotation> annotationType, F field);

// ...

    /**
     * Gets all the annotations on a field.
     */
    Annotation[] getAllFieldAnnotations(F field, Locatable srcPos);

// ...

}

然后,您可以在创建 JAXB 上下文时使用阅读器:

final AnnotationReader<Type, Class, Field, Method> annotationReader = new MyCustomAnnotationReader();

final Map<String, Object> properties = new HashMap<String, Object>();

properties.put(JAXBRIContext.ANNOTATION_READER, annotationReader);

或者考虑使用MOXy XML bindings而不是注释。

关于java - JAXB 中的非法 XML/异常取消/编码影子变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33159260/

相关文章:

java - JDBC(postgreSql)如何导出/导入java.sqlTime而没有毫秒精度损失

xml - XSLT 匹配和命名空间

java - 为不同包中的任何类全局注册 XmlAdapter

java - 如何查看这个序列化的xml内容?

java - 对象列表的 JSON 结构

c# - C# 和 Java 应该合并彼此的语法吗?

java - 如何构建泛型类和构造函数以使用泛型方法?

java - 在插件中和通过 Tomcat 覆盖 Spring Boot application.properties 属性?

xml - 使用带有命名空间的 Freemarker DocumentModel

xml - IXMLNode 设置前缀和命名空间