我正在尝试编写该类,以便在JAXB中编写具有属性的元素。在这个XML中,有一些默认值,无论它们是字符串,整数还是自定义类类型。
以下减少的例子:
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "FIELD")
public class TestLayoutNode
{
// I want to not write this to the xml when it is 0
@XmlAttribute(name = "num")
private int number;
// I want to not write this when it is "default"
@XmlAttribute(name = "str")
private String str;
}
根据JAXB Avoid saving default values 我知道如果我不想写字符串我可以修改getters / setters来写null并读取默认值,如果它读取为null。
然而,使用int我不知道该怎么做,因为它总是具有值0,除非它被特别更改。
有更好的方法吗?我可以将内部数据类型更改为String,然后在需要时将其强制转换,但这有点混乱。
答案 0 :(得分:3)
您可以通过将字段更改为对象类型来执行以下操作(默认情况下,空值不会出现在XML表示中)并在getter中添加一些逻辑:
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "FIELD")
public class TestLayoutNode
{
@XmlAttribute(name = "num")
private Integer number;
@XmlAttribute
private String str;
public int getNumber() {
if(null == number) {
return 0;
} else {
return number;
}
}
public void setNumber(int number) {
this.number = number;
}
public String getStr() {
if(null == str) {
return "default";
} else {
return str;
}
}
public void setStr(String str) {
this.str = str;
}
}
如果要允许set操作将属性返回到其默认状态,则需要在set方法中添加逻辑。
public void setNumber(int number) {
if(0 == number) {
this.number = null;
} else {
this.number = number;
}
}
或者你可以提供一个未设置的方法:
public void unsetNumber() {
this.number = null;
}
null
如果您希望将str
属性设置为null
,以便get方法将返回null
而不是"default"
,那么您可以保留一个标记跟踪是否已设置:
private strSet = false;
public String getStr() {
if(null == str && !strSet) {
return "default";
} else {
return str;
}
}
public void setStr(String str) {
this.str = str;
this.strSet = true;
}
Blaise,难道你不认为解决方案非常详细吗?
是
我的意思是这样的用例可能应该由框架支持。 例如,使用@DefaultValue等注释。
JAXB今天如何支持默认值
如果XML中没有节点,则不对Java对象中的相应字段/属性执行集合。这意味着你初始化的财产价值仍然存在。在填充值后的元帅上,它将被整理出来。
真正被问到的是什么
真正要求的是当字段/属性具有默认值时不对其进行编组。通过这种方式,您希望marshal行为对于null和default值是相同的。这引入了一些有待解决的问题:
人们今天做什么?
通常对于此用例,人们只需将int
属性更改为Integer
,并将null
作为默认值。我之前没有遇到有人要求String
这种行为。
答案 1 :(得分:1)
您可以更改为整数
private Integer number;
然后,在未实例化时,对象的值将为null。
答案 2 :(得分:1)
使用Integer
代替原始int
。将所有基元类型替换为对象,然后使用NULL
。
根据字符串默认值,使用并修改getter
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "FIELD")
public class NullAttrs {
private Integer number;
private String str;
public void setNumber(Integer number) {
this.number = number;
}
@XmlAttribute(name = "num")
public Integer getNumber() {
return number;
}
public void setStr(String str) {
this.str = str;
}
@XmlAttribute(name = "str")
public String getStr() {
if (str != null && str.equalsIgnoreCase("default"))
return null;
else if (str == null)
return "default";
else
return str;
}
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(NullAttrs.class);
NullAttrs root = new NullAttrs();
root.setNumber(null);
root.setStr("default");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
在这种情况下,结果为空FIELD
:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIELD/>
答案 3 :(得分:0)
虽然它不像人们希望的那样简洁,但可以创建XmlAdapters以避免编组默认值。
用例是这样的:
@XmlRootElement(name = "FIELD")
public class TestLayoutNode
{
@XmlAttribute(name = "num")
@XmlJavaTypeAdapter(value = IntegerZero.class, type = int.class)
public int number;
@XmlAttribute(name = "str")
@XmlJavaTypeAdapter(StringDefault.class)
public String str = "default";
}
这是适配器。
IntegerZero:
public class IntegerZero extends DefaultValue<Integer>
{
public Integer defaultValue() { return 0; }
}
StringDefault:
public class StringDefault extends DefaultValue<String>
{
public String defaultValue() { return "default"; }
}
DefaultValueAdapter:
public class DefaultValue<T> extends XmlAdapter<T, T>
{
public T defaultValue() { return null; }
public T marshal(T value) throws Exception
{
return (value == null) || value.equals(defaultValue()) ? null : value;
}
public T unmarshal(T value) throws Exception
{
return value;
}
}
使用少量不同的默认值,这种方法效果很好。
答案 4 :(得分:0)
我发现使用自定义getter / setter或适配器的解决方案令人讨厌,所以我选择了另一种解决方案:一个检查值的编组器,如果它们是默认值则将它们清空。
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Set;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.helpers.AbstractMarshallerImpl;
import javax.xml.transform.Result;
import com.google.common.collect.ImmutableSet;
class MyJaxbMarshaller extends AbstractMarshallerImpl {
/** See https://docs.oracle.com/cd/E13222_01/wls/docs103/webserv/data_types.html#wp221620 */
private static final Set<String> SUPPORTED_BASIC_TYPES = ImmutableSet.of(
"boolean", "java.lang.Boolean", "byte", "java.lang.Byte", "double", "java.lang.Double",
"float", "java.lang.Float", "long", "java.lang.Long", "int", "java.lang.Integer",
"javax.activation.DataHandler", "java.awt.Image", "java.lang.String",
"java.math.BigInteger", "java.math.BigDecimal", "java.net.URI", "java.util.Calendar",
"java.util.Date", "java.util.UUID", "javax.xml.datatype.XMLGregorianCalendar",
"javax.xml.datatype.Duration", "javax.xml.namespace.QName",
"javax.xml.transform.Source", "short", "java.lang.Short");
private final Marshaller delegate;
MyJaxbMarshaller(Marshaller delegate) {
this.delegate = delegate;
}
@Override
public void setProperty(String name, Object value) throws PropertyException {
super.setProperty(name, value);
delegate.setProperty(name, value);
}
@Override
public void marshal(Object jaxbElement, Result result) throws JAXBException {
try {
delegate.marshal(clearDefaults(jaxbElement), result);
} catch (ReflectiveOperationException ex) {
throw new JAXBException(ex);
}
}
private Object clearDefaults(Object element) throws ReflectiveOperationException {
if (element instanceof Collection) {
return clearDefaultsFromCollection((Collection<?>) element);
}
Class<?> clazz = element.getClass();
if (isSupportedBasicType(clazz)) {
return element;
}
Object adjusted = clazz.getConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
copyOrRemove(field, element, adjusted);
}
return adjusted;
}
private Object clearDefaultsFromCollection(Collection<?> collection)
throws ReflectiveOperationException {
@SuppressWarnings("unchecked")
Collection<Object> result = collection.getClass().getConstructor().newInstance();
for (Object element : collection) {
result.add(clearDefaults(element));
}
return result;
}
private static boolean isSupportedBasicType(Class<?> clazz) {
return SUPPORTED_BASIC_TYPES.contains(clazz.getName());
}
private void copyOrRemove(Field field, Object element, Object adjusted)
throws ReflectiveOperationException {
Object value = field.get(element);
if (value != null) {
if (value.equals(field.get(adjusted))) {
value = null;
} else {
value = clearDefaults(value);
}
}
field.set(adjusted, value);
}
}
这适用于像
这样的类@XmlRootElement
public class Foo {
@XmlAttribute public Integer intAttr = 0;
@XmlAttribute public String strAttr = "default";
}
如果您愿意,可以使其更加灵活,例如:您可以使用注释来标记要在默认情况下省略的属性,或者扩展类以了解@XmlTransient
或方法访问器(现在我的项目中都不存在问题)
您为绑定类的简单性付出的代价是,编组器将创建您要编组的对象的深层副本,并对默认值进行大量比较以确定要取消的内容。因此,如果运行时性能对您来说是个问题,那么这可能是不行的。