web-dev-qa-db-ja.com

null値をXML jaxbの空の要素として表す

Jaxbで空の要素としてnull値を表示する必要があります。私はjaxbのmoxy実装を使用しています。このオプションを見つけました

@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE)

クラスレベルで適用できる同様の拡張機能はありますか(クラスで定義されているすべての要素に対して)

11
Anand B

ノードがないか、またはxsi:nil="true"属性でnullを表すことを強くお勧めします。これは、スキーマ検証で最も効果的に機能します(つまり、<age/>または<age></age>は、タイプxsd:intの有効な要素ではありません。ただし、ここにできない場合は、次のようにしてユースケースを実現できます。

標準JAXB動作

標準のAPIを使用すると、nullが不在ノードとして表されるか、xsi:nil="true"注釈に@XmlElement注釈が付けられるかを制御できます(参照: http://blog.bdoughan.com/2012/04 /binding-to-json-xml-handling-null.html )。

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {

    private String street;

    @XmlElement(nillable=true)
    private String city;

}

以下は、両方のフィールドの値がnullの場合のXML出力です。

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

MOXy-クラスごとのこの動作のオーバーライド

MOXyは、クラスのすべてのプロパティにnullポリシーを指定するアノテーションを提供していません。ただし、@XmlCustomizerアノテーションを使用してDescriptorCustomizerを活用し、ネイティブのMOXyマッピングメタデータを微調整して同じことを行うことができます。

DescriptorCustomizer(AddressCustomizer)

import org.Eclipse.persistence.config.DescriptorCustomizer;
import org.Eclipse.persistence.descriptors.ClassDescriptor;
import org.Eclipse.persistence.mappings.DatabaseMapping;
import org.Eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.Eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;

public class AddressCustomizer implements DescriptorCustomizer {

    @Override
    public void customize(ClassDescriptor descriptor) throws Exception {
        for(DatabaseMapping mapping : descriptor.getMappings()) {
            if(mapping.isAbstractDirectMapping()) {
                XMLDirectMapping xmlDirectMapping = (XMLDirectMapping) mapping;
                xmlDirectMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.EMPTY_NODE);
                xmlDirectMapping.getNullPolicy().setNullRepresentedByEmptyNode(true);
            }
        }
    }

}

DomainModel(アドレス)

import javax.xml.bind.annotation.*;
import org.Eclipse.persistence.oxm.annotations.XmlCustomizer;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(AddressCustomizer.class)
public class Address {

    private String street;

    @XmlElement(nillable=true)
    private String city;

}

出力

<?xml version="1.0" encoding="UTF-8"?>
<address>
   <street/>
   <city/>
</address>

MOXy-すべてのクラスでこの動作を無効にする

代わりに、マップされたすべてのクラスのnull処理をオーバーライドする場合は、代わりにSessionEventListenerを使用することをお勧めします。必要に応じて、このアプローチを使用して単一クラスのメタデータを更新することもできます。

SessionEventListener(NullPolicySessionEventListener)

import org.Eclipse.persistence.descriptors.ClassDescriptor;
import org.Eclipse.persistence.mappings.DatabaseMapping;
import org.Eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.Eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
import org.Eclipse.persistence.sessions.*;

public class NullPolicySessionEventListener extends SessionEventAdapter {

    @Override
    public void preLogin(SessionEvent event) {
        Project project = event.getSession().getProject();
        for(ClassDescriptor descriptor : project.getOrderedDescriptors()) {
            for(DatabaseMapping mapping : descriptor.getMappings()) {
                if(mapping.isAbstractDirectMapping()) {
                    XMLDirectMapping xmlDirectMapping = (XMLDirectMapping) mapping;
                    xmlDirectMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.EMPTY_NODE);
                    xmlDirectMapping.getNullPolicy().setNullRepresentedByEmptyNode(true);
                }
            }
        }
     }

}

デモコード

import Java.util.*;
import javax.xml.bind.*;
import org.Eclipse.persistence.jaxb.JAXBContextProperties;
import org.Eclipse.persistence.sessions.SessionEventListener;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(1);
        SessionEventListener sessionEventListener = new NullPolicySessionEventListener();
        properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, sessionEventListener);
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Address.class}, properties);

        Address address = new Address();

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(address, System.out);
    }

}

出力

<?xml version="1.0" encoding="UTF-8"?>
<address>
   <street/>
   <city/>
</address>
18
bdoughan

クラスにStringフィールドしかない場合の「悪い習慣」の回避策は、次のように要素のセッターをオーバーライドすることです。

public class Address {

  private String street;

  @XmlElement(name = "street")
  public void setStreet(String street){
    this.street = street;
    if (this.street == null){
      this.street = ""; // Not NULL, empty string like new String()!
    }
  }
}

これは日付や数値などの他のタイプでは機能しません!しかし、時にはStringで十分です。

@Blaise Doughanの回答は、対処できれば、長期的にははるかに良くなります。 :)

0
Laszlo Lugosi