Java unmarshalling - NULL or missing tag?

I have a field without fields in a class defined by unmarshaller:

@XmlElement(name = "value", nillable = true) private BigDecimal valueVariable; 

My problem is that I cannot determine if the xml element was omitted or set to nil:

a. the <value/> element is not in the XML file; this is not required.
=> (value Variable == null) true

C. The XML file contains <value xsi:nil="true"/>
=> (value Variable == null) true

How can I indicate for a non-String variable if the value is xsi:nil or the tag is missing?

UPDATE You can see 2 good solutions, I preferred one of them, but the other will be good too!

+6
source share
3 answers

JAXB implementations (JSR-222) can represent null either as a missing node or as a null element based on setting nillable to @XmlElement . When you need to support both, or distinguish between the two, you can use JAXBElement .

Java Model

Root

Fields / properties of type JAXBElement displayed with the annotation @XmlElementRef . This corresponds to the @XmlElementDecl annotation for the class annotated with @XmlRegistry .

 import java.math.BigDecimal; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Root { @XmlElementRef(name="foo") JAXBElement<BigDecimal> foo; @XmlElementRef(name="bar") JAXBElement<BigDecimal> bar; @XmlElementRef(name="baz") JAXBElement<BigDecimal> baz; } 

ObjectFactory

 import java.math.BigDecimal; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; import javax.xml.namespace.QName; @XmlRegistry public class ObjectFactory { @XmlElementDecl(name = "foo") public JAXBElement<BigDecimal> createFoo(BigDecimal value) { return new JAXBElement<BigDecimal>(new QName("foo"), BigDecimal.class, value); } @XmlElementDecl(name = "bar") public JAXBElement<BigDecimal> createBar(BigDecimal value) { return new JAXBElement<BigDecimal>(new QName("bar"), BigDecimal.class, value); } @XmlElementDecl(name = "baz") public JAXBElement<BigDecimal> createBaz(BigDecimal value) { return new JAXBElement<BigDecimal>(new QName("baz"), BigDecimal.class, value); } } 

Demo code

Input.xml

 <?xml version="1.0" encoding="UTF-8"?> <root> <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/> <baz>123.456</baz> </root> 

Demo

Below is some demo code that you can run to show that everything is working. Note how the JAXBIntrospector can be used to get the actual value by JAXBElement if necessary.

 import java.io.File; import java.math.BigDecimal; import javax.xml.bind.*; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); File xml = new File("src/forum18440987/input.xml"); Root root = (Root) unmarshaller.unmarshal(xml); nullOrAbsent("foo", root.foo); nullOrAbsent("bar", root.bar); nullOrAbsent("baz", root.baz); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root, System.out); } private static void nullOrAbsent(String property, JAXBElement<BigDecimal> value) { System.out.print(property); if(null == value) { System.out.print(": ABSENT - "); } else if(value.isNil()) { System.out.print(": NIL - "); } else { System.out.print(": VALUE - "); } System.out.println(JAXBIntrospector.getValue(value)); } } 

Output

 foo: ABSENT - null bar: NIL - null baz: VALUE - 123.456 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <root> <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/> <baz>123.456</baz> </root> 

UPDATE

If you want to keep the existing get / set methods, you can save access to the fields, as I have in this answer, and change the access methods to look like this:

 public BigDecimal getBar() { if(null == bar) { return null; } return bar.getValue(); } public void setBar(BigDecimal bar) { if(null == this.bar) { this.bar = new JAXBElement<BigDecimal>(new QName("bar"), BigDecimal.class, bar); } else { this.bar.setValue(bar); } } 

Alternatively, you can add the isSet method to find out if a value has been set.

 public boolean isSetBar() { return null != bar; } 

This approach does not require you to have access to Unmarshaller . To make sure that the ObjectFactory selected, you can use the @XmlSeeAlso annotation to reference it from one of your domain classes.

 @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) @XmlSeeAlso(ObjectFactory.class) public class Root { 
+4
source

There is a similar question here: Handling missing nodes using JAXB . It seems that if the element is missing, the setter method will not be called. Therefore, you can put some logic in the method to determine if the node is missing or has an empty value.

 @XmlElement(name = "value", nillable = true) private BigDecimal valueVariable; private boolean valueVariableMissing = true; public void setValueVariable(BigDecimal valueVariable){ this.valueVariable = valueVariable; this.valueVariableMissing = false; } 
+1
source

I was dealing with the same problem, To fix this, I created a new file in the package, I was marshaling the object:
"Package-info.java" There I added:

 @javax.xml.bind.annotation.XmlSchema(namespace = "http://the.correct.namespace.com/gateway/", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package com.the.package.name; 

Now everything is all right

0
source

Source: https://habr.com/ru/post/952489/


All Articles