Search code examples
javaxmljaxbxmladapter

Using JAXB to unmarshal a xml string but get empty one


Problem solved

After change the XmlElementRef annotation to @XmlElementRef(name = "Option", type = Option.class, required = false) and add @XmlRootElement(name = "Option") to Option.class


Update:

I have added two classes(Options and Option) as laune's recommend and modified the GuiMOProperties class as below

@XmlAccessorType(XmlAccessType.FIELD)
public class GuiMOProperties
{
    @XmlElement(name="Grouping")
    private boolean                 grouping;
    @XmlElement(name="Type")
    private String                  type;
    @XmlElement(name="Regex")
    private String                  regex;
    @XmlElement(name="DisplayName")
    private String                  displayName;
    @XmlElement(name="Options")
    private Options                 options;
    @XmlElement(name="Mask")
    private String                  mask;
    @XmlElement(name="DefaultValue")
    private String                  defaultValue;
}

But got some exception when I have to new an instance of JAXBContext as below:

JAXBContext jaxbContext = JAXBContext.newInstance(GuiMOProperties.class);


java.lang.IllegalArgumentException: can't parse argument number: ''{0}''
at java.text.MessageFormat.makeFormat(MessageFormat.java:1429)
at java.text.MessageFormat.applyPattern(MessageFormat.java:479)
at java.text.MessageFormat.<init>(MessageFormat.java:362)
at java.text.MessageFormat.format(MessageFormat.java:840)
at com.sun.xml.internal.bind.v2.model.impl.Messages.format(Messages.java:117)
at com.sun.xml.internal.bind.v2.model.impl.ReferencePropertyInfoImpl.calcTypes(ReferencePropertyInfoImpl.java:171)
at com.sun.xml.internal.bind.v2.model.impl.ReferencePropertyInfoImpl.link(ReferencePropertyInfoImpl.java:372)
at com.sun.xml.internal.bind.v2.model.impl.ClassInfoImpl.link(ClassInfoImpl.java:1257)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeClassInfoImpl.link(RuntimeClassInfoImpl.java:182)
at com.sun.xml.internal.bind.v2.model.impl.ModelBuilder.link(ModelBuilder.java:439)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder.link(RuntimeModelBuilder.java:118)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:443)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:277)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:124)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1123)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:147)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:247)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:234)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:462)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:641)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
at devicemanage.utility.Tree2xml.omElementToGuiMOPropertiesList(Tree2xml.java:332)
at devicemanage.service.appdeploy.DeviceMgtServiceImpl.getMOProperties(DeviceMgtServiceImpl.java:250)
at devicemanage.service.appdeploy.DeviceMgtServiceImpl.access$0(DeviceMgtServiceImpl.java:238)
at devicemanage.service.appdeploy.DeviceMgtServiceImpl$1.load(DeviceMgtServiceImpl.java:233)
at devicemanage.service.appdeploy.DeviceMgtServiceImpl$1.load(DeviceMgtServiceImpl.java:1)
at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527)
at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2319)
at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2282)
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2197)
at com.google.common.cache.LocalCache.get(LocalCache.java:3937)
at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3941)
at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4824)
at devicemanage.service.appdeploy.DeviceMgtServiceImpl.findMOPropertiesByDeviceType(DeviceMgtServiceImpl.java:208)
at devicemanage.service.appdeploy.DeviceMgtServiceImpl$$FastClassBySpringCGLIB$$1a89f063.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:266)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at devicemanage.service.appdeploy.DeviceMgtServiceImpl$$EnhancerBySpringCGLIB$$97274215.findMOPropertiesByDeviceType(<generated>)
at TestDeviceMgtService.testFindMOPropertiesByDeviceType(TestDeviceMgtService.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:72)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:81)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:216)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:82)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:60)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:67)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:162)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.NumberFormatException: For input string: "''{0}''"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:569)
    at java.lang.Integer.parseInt(Integer.java:615)
    at java.text.MessageFormat.makeFormat(MessageFormat.java:1427)
    ... 78 more

I have a class defined as below for JAXB parsing:

@XmlAccessorType(XmlAccessType.FIELD)
public class GuiMOProperties
{
    @XmlElement(name="Grouping")
    private boolean                 grouping;
    @XmlElement(name="Type")
    private String                  type;
    @XmlElement(name="Regex")
    private String                  regex;
    @XmlElement(name="DisplayName")
    private String                  displayName;
    @XmlElement(name="Options")
    @XmlJavaTypeAdapter(OptionsAdapter.class)
    private GuiMOOption                 options;
    @XmlElement(name="Mask")
    private String                  mask;
    @XmlElement(name="DefaultValue")
    private String                  defaultValue;
}

And the xml string I have to parsed as below:

            <VolumeUpKey>
                <Type/>
                <Regex/>
                <DisplayName/>
                <Options>testOptions</Options>
                <Mask/>
                <DefaultValue/>
            </VolumeUpKey>
            <SSID>
                <Type/>
                <Regex/>
                <DisplayName/>
                <Options>
                    <Option name="1" value="1"></Option>
                    <Option name="2" value="2"></Option>
                    <Option name="3" value="3"></Option>
                    <Option name="4" value="4"></Option>
                </Options>
                <Mask/>
                <DefaultValue/>
            </SSID>

In the unmarshal method of my OptionsAdapter.class, the first one Options element works fine, there has a string "testOptions" input. However, the second one Options element didn't. The input paramater will be come \n\t\t\t\t, but not a string <Option name="1" value="1"></Option><Option name="2" value="2"></Option><Option name="3" value="3"></Option><Option name="4" value="4"></Option>.

The OptionsAdapter.class as below

public class OptionsAdapter extends XmlAdapter<String, GuiMOOption>
{
    @Override
    public GuiMOOption unmarshal(String v) throws Exception
    {
        try
        {
            System.out.println("unmarshal string : " + v);
        } catch (Exception e)
        {
            // TODO: handle exception
        }

        return null;
    }

    @Override
    public String marshal(GuiMOOption v) throws Exception
    {
        // TODO Auto-generated method stub
        return null;
    }
}

Any help is appreciated.


Solution

  • Mixed content isn't quite straightforward, although JAXB can handle it without an adapter.

    You can define the type of the options field as Options (instead of GuiMOOption):

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "Options", propOrder = {
        "content"
    })
    public class Options {
    
        @XmlElementRef(name = "field", type = JAXBElement.class, required = false)
        @XmlMixed
        protected List content;
    
        public List getContent() {
            if (content == null) {
                content = new ArrayList();
            }
            return this.content;
        }
    }
    

    The List will contain elements of type JAXBElement<Option> and String, where Option is a class

    @XmlAccessorType(XmlAccessType.FIELD)
    public class Option {
        @XmlAttribute
        protected String name;
        @XmlAttribute
        protected String value;
        //...
    }
    

    You have to iterate the List to learn whether you have the first or the second XML form. In the first case there'll be just one String. In the second case, there may be white space strings among the wrapped Option objects.