Why does @XmlJavaTypeAdapter on the root object not work with Marshaller.marshal in JDK 8?

1 day ago 1
ARTICLE AD BOX

Following up on the previous question (How to use XmlAdapter correctly?). After solving my solution by using fastxml,I was curious about why jaxb didn't work.For this reason,I cheaked the source code.The following is my discovery.

First of all,I found the place where the error was thrown by tracing the marshal. The method that threw the error is com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot. The reason for the error is that tagName is null and there is no XmlRootElement annotation.

I tried to find the timing of assigning a value to tagName and found that it was assigned when calling JAXBContext.newInstance.

At its core, it uses reflection to call com.sun.xml.internal.bind.v2.ContextFactory.createContext, and further instantiates JAXBContextImpl by calling JAXBContextImpl.JAXBContextBuilder.build.Within the constructor of JAXBContextImpl, getTypeInfoSet is called. Inside this method, an object of type RuntimeModelBuilder named builder calls getTypeInfo. Through multiple layers of calls, it eventually calls com.sun.xml.internal.bind.v2.model.impl.ModelBuilder.getClassInfo, where internal checks are performed. Since it is the first creation and it is a regular Java Bean, createClassInfo will be called further.

In the constructor of com.sun.xml.internal.bind.v2.model.impl.ClassInfoImpl, it parses the element name through the parseElementName method. If this class does not have the XmlRootElement.class annotation, it will return null directly, elementName will be used to accept null.

When the getTypeInfoSet call ends and enters the for loop prepared for ordinary java bean (that is, the third for loop), it will obtain or instantiate com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl by calling the getOrCreate method. When I enter this method and further enter the constructor of com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl, it assigns a value to tagName by calling the ci.isElement(ci is of type RuntimeClassInfo) method to check if it is true. When it is false, tagName is null.

And in the isElement method, a check is made to see whether elementName is null.

The version I am using is jdk8u472-b08.

Question

During my debugging process, it seems that I did not find any handling for the adapter, perhaps because I did not notice it due to my lack of skill.

If someone has successfully used it, could you share the source code?

Below are my test codes.

package com.ff; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "root") public class MiddleRoot { private Head head; private Body body; public MiddleRoot() { } public MiddleRoot(Head head, Body body) { this.head = head; this.body = body; } public Head getHead() { return head; } public void setHead(Head head) { this.head = head; } public Body getBody() { return body; } public void setBody(Body body) { this.body = body; } public String toString() { return "MiddleRoot{head = " + head + ", body = " + body + "}"; } public static class Head{ private String a; private String b; private String c; public Head() { } public Head(String a, String b, String c) { this.a = a; this.b = b; this.c = c; } public String getA() { return a; } public void setA(String a) { this.a = a; } public String getB() { return b; } public void setB(String b) { this.b = b; } public String getC() { return c; } public void setC(String c) { this.c = c; } public String toString() { return "Head{a = " + a + ", b = " + b + ", c = " + c + "}"; } } public static class Body{ private String d; private String e; public Body() { } public Body(String d, String e) { this.d = d; this.e = e; } public String getD() { return d; } public void setD(String d) { this.d = d; } public String getE() { return e; } public void setE(String e) { this.e = e; } public String toString() { return "Body{d = " + d + ", e = " + e + "}"; } } } package com.ff; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlJavaTypeAdapter(RootAdapter.class) public class Root { private String a; private String b; private String c; private String d; private String e; public Root() { } public Root(String a, String b, String c, String d, String e) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; } public String getA() { return a; } public void setA(String a) { this.a = a; } public String getB() { return b; } public void setB(String b) { this.b = b; } public String getC() { return c; } public void setC(String c) { this.c = c; } public String getD() { return d; } public void setD(String d) { this.d = d; } public String getE() { return e; } public void setE(String e) { this.e = e; } public String toString() { return "Root{a = " + a + ", b = " + b + ", c = " + c + ", d = " + d + ", e = " + e + "}"; } } package com.ff; import javax.xml.bind.annotation.adapters.XmlAdapter; public class RootAdapter extends XmlAdapter<MiddleRoot,Root> { @Override public Root unmarshal(MiddleRoot v) throws Exception { MiddleRoot.Head head = v.getHead(); MiddleRoot.Body body = v.getBody(); Root r = new Root(); if(head!=null){ r.setA(head.getA()); r.setB(head.getB()); r.setC(head.getC()); } if(body!=null){ r.setD(body.getD()); r.setE(body.getE()); } return r; } @Override public MiddleRoot marshal(Root v) throws Exception { MiddleRoot.Head head = new MiddleRoot.Head(v.getA(), v.getB(), v.getC()); MiddleRoot.Body body = new MiddleRoot.Body(v.getD(), v.getE()); return new MiddleRoot(head,body); } } package com.ff; import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception { Root root = new Root("a", "b", "c", "d", "e"); JAXBContext context = JAXBContext.newInstance(Root.class,MiddleRoot.class); context = (JAXBContextImpl)context; Field classes = context.getClass().getDeclaredField("classes"); classes.setAccessible(true); Class[] classes1 = (Class[]) classes.get(context); for (Class aClass : classes1) { System.out.println("class Name:"+aClass.getName()); Annotation[] annotations = aClass.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("type:"+annotation.annotationType().getSimpleName()); System.out.println("name:"+annotation.annotationType().getName()); Method[] declaredMethods = annotation.annotationType().getDeclaredMethods(); for (Method method : declaredMethods) { Object value = method.invoke(annotation); System.out.println(" "+method.getName()+" = "+value); } System.out.println(); } } try { Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING,"UTF-8"); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root,System.out); } catch (JAXBException e) { System.out.println(e); } } }
Read Entire Article