1

JAXB を使用して着信 SOAP ドキュメントをドメイン クラスにアンマーシャリングする Web サービスの作業を始めたところです。デンマークの政府機関で使用されている OIO XML 形式によって規定されている技術的な問題に直面しました。この形式では、xml 要素の宣言に nillable な xml スキーマ属性を使用することは許可されていないことが特に明記されています。したがって、私は自分の課題に対する別の解決策を見つけなければなりません。

説明 アプリケーションを更新するために Web サービス クライアントから送信される可能性のある数字と日付がいくつかあります。これらの数値と日付は、同等のタイプの POJO フィールドにマップされます。課題は、正しい XML を構築して送信することにより、そのような POJO フィールドの値をリセットする方法です。

12:31:34T01-01-2010..... を送信すると、POJO フィールドが指定された値に更新されます。

ただし、日時要素では許可されていないため、送信してフィールドをリセットすることはできません。

また、OIO XML 規格で許可されていないため送信できません。

したがって、OIO XML 標準で禁止されるべきではないため、厳しい回避策として送信する予定です。

これにより、startTime 要素に delete="true" 属性が含まれている場合、対応する POJO フィールドを null に設定する必要があるという課題が生じます。a に削除属性がない場合は、有効な要素の値を POJO フィールドに転送します。

@XMLElement アノテーションでは、startTime 値のみをマップできます。たとえば、

class MyClass{
   @XMLElement
   private Date startTime;
}

削除属性が MyClass.startTime フィールドの値にも影響を与えるように強制するにはどうすればよいですか?

よろしく、ジェスパー

4

5 に答える 5

1

2 番目の質問への回答:

(EclipseLink 2.1.1 を使用して) コードを実行すると、次の出力が得られます。

20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder find
FINE: Trying to locate forum80/jaxb.properties
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder loadJAXBProperties
FINE: loading props from file:/C:/Workspaces/EclipseLink-Trunk/SCRATCH/bin/forum80/jaxb.properties
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder find
FINE:   found
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder safeLoadClass
FINE: Trying to load org.eclipse.persistence.jaxb.JAXBContextFactory
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder newInstance
FINE: loaded org.eclipse.persistence.jaxb.JAXBContextFactory from jar:file:/C:/Workspaces/EclipseLink-2.1/eclipselink.jar!/org/eclipse/persistence/jaxb/JAXBContextFactory.class
UNMARSHALL: DeletableString[delete=true, value=010]
SimpleNavne[, forNavn=null, fornavneMrkKode=011]

この結果は、表示されているものとは異なります。

SimpleNavne[forNavn=010, fornavneMrkKode=]

特定の XML サンプルを処理するために、XML アダプターのロジックを微調整する必要がある場合があります。XML フラグメントで、delete="true" と、"forNavn" プロパティにマップされる "PersonGivenName" 要素の値の両方を指定します。この場合、null または値が優先されますか?

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns:xml-fragment xmlns:ns="http://cpr.csc.com/navne">
    <ns:PersonGivenName delete="true">010</ns:PersonGivenName>
    <ns:PersonGivenNameMarkingStructure>
        <ns:MarkingCode>011</ns:MarkingCode>
    </ns:PersonGivenNameMarkingStructure>
</ns:xml-fragment>

JAXB RI が使用されている場合、「fornavneMrkKode」プロパティが空の文字列を返すようにすることができます。EclipseLink MOXy を使用するために jaxb.properties が正しく指定されていない可能性はありますか?

jaxb.properties ファイルは、次のエントリを持つモデル クラスと同じパッケージに存在する必要があります。

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

アップデート:

問題のデバッグに使用している Java プロジェクトをメールで送信しました。実行して同じ結果が得られるかどうかを確認していただけますか? 同じ出力が得られ、不正な動作の原因となっているデルタを特定できることを願っています。

于 2010-10-20T19:04:05.427 に答える
1

ご回答ありがとうございます。非常に単純なシナリオではうまくいくようですが、もう少し複雑なシナリオではうまくいきません。

私は自分のドメインであなたのコードを模倣しようとしましたが、データをマーシャリングではなくアンマーシャリングしているだけです。

アダプタ クラス:

public class DeleteStringAdapter extends XmlAdapter<DeletableString, String> {

@Override
public DeletableString marshal(String value) throws Exception {
    System.out.println("MARSHALL: " + value);
    return new DeletableString(value);
}

@Override
public String unmarshal(DeletableString v) throws Exception {
    System.out.println("UNMARSHALL: "  + v);

    if(v.isDelete() != null && v.isDelete()){
        return null;
    }

    return v.getValue();    
}

}

アダプターの種類:

public class DeletableString {

public DeletableString() {
}

public DeletableString(String value) {
    this.value = value;
}

private Boolean delete;

private String value;

@XmlAttribute
public Boolean isDelete() {
    return delete;
}

public void setDelete(Boolean delete) {
    this.delete = delete;
}   

@XmlValue
public String getValue() {
    return value;
}

public void setValue(String value) {
    this.value = value;
}

@Override
public String toString() {
    return DeletableString.class.getSimpleName() + "[delete=" + isDelete() + ", value=" + value + "]";
}
}

注釈付きの作業ドメイン クラス:

@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement(name = "xml-fragment")
public class SimpleNavne implements Serializable{

private String forNavn = "";

private String fornavneMrkKode = "";

@XmlElement(name="PersonGivenName")
@XmlJavaTypeAdapter(value = DeleteStringAdapter.class)
public String getForNavn() {
    return forNavn;
}

public void setForNavn(String forNavn) {
    this.forNavn = forNavn;
}

@Override
public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append(SimpleNavne.class.getSimpleName() + "[");
    builder.append("forNavn=");
    builder.append(forNavn);
    builder.append("]");
    return builder.toString();
}

}

デモ

public class AppTest {

@Test
public void testApp() throws Exception {

    System.setProperty("jaxb.debug", "true");

    try{
        JAXBContext jaxbContext = JAXBContext.newInstance(SimpleNavne.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        Object ps =  unmarshaller.unmarshal(new File("./personname-test4.xml"));

        System.out.println(ps);

    }catch(Exception e){
        e.printStackTrace();
    }
}
}

ファイル:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns:xml-fragment xmlns:ns="http://cpr.csc.com/navne">
<ns:PersonGivenName delete="false">010</ns:PersonGivenName>
</ns:xml-fragment>

出力から結論付けられた場合、上記は正常に機能するようです

SimpleNavne[forNavn=010]

ただし、SimpleName ドメイン クラスに @XMLPath で注釈が付けられたフィールドを導入すると問題が発生します。

変更されたドメイン クラス

@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement(name = "xml-fragment")
public class SimpleNavne implements Serializable{

private String forNavn = "";

private String fornavneMrkKode = "";

@XmlElement(name="PersonGivenName")
@XmlJavaTypeAdapter(value = DeleteStringAdapter.class)
public String getForNavn() {
    return forNavn;
}

public void setForNavn(String forNavn) {
    this.forNavn = forNavn;
}

@XmlPath("/PersonGivenNameMarkingStructure/MarkingCode/text()")
public String getFornavneMrkKode() {
    return fornavneMrkKode;
}

public void setFornavneMrkKode(String forNavnMrk) {
    this.fornavneMrkKode = forNavnMrk;
}

@Override
public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append(SimpleNavne.class.getSimpleName() + "[");
    builder.append(", forNavn=");
    builder.append(forNavn);
    builder.append(", fornavneMrkKode=");
    builder.append(fornavneMrkKode);
    builder.append("]");
    return builder.toString();
}

}

変更されたファイル:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns:xml-fragment xmlns:ns="http://cpr.csc.com/navne">
<ns:PersonGivenName delete="true">010</ns:PersonGivenName>
<ns:PersonGivenNameMarkingStructure>
    <ns:MarkingCode>011</ns:MarkingCode>
</ns:PersonGivenNameMarkingStructure>
</ns:xml-fragment>

出力は次のとおりです。

SimpleNavne[forNavn=010, fornavneMrkKode=]

しかし、次のようになっているはずです:

SimpleNavne[forNavn=010, fornavneMrkKode=011]

私は何か完全に間違っていますか、それとも MOXy はこのシナリオをサポートしていませんか?

PS。MOXy 2.1.1 と 2.2.0-M3 を使用してみましたが、同じ結果が得られました

于 2010-10-20T15:25:05.553 に答える
1

この度はお時間を割いていただき、誠にありがとうございました。

最後の回答への回答として: JAXB/MOXy は要素の値と要素の属性を同じ POJO フィールドにマップできますか?

工場出荷時の設定で jaxb.properties ファイルを使用しています。私の出力は、欠落している値を除いて、あなたのものとほとんど同じに見えます:

21-10-2010 09:47:08 javax.xml.bind.ContextFinder find
FINE: Trying to locate com/csc/cpr/ws/domain/jaxb.properties
21-10-2010 09:47:08 javax.xml.bind.ContextFinder loadJAXBProperties
FINE: loading props from file:/C:/Ajour/ajourworkspace/jaxb-test-with-schema/target/classes/com/csc/cpr/ws/domain/jaxb.properties
21-10-2010 09:47:08 javax.xml.bind.ContextFinder find
FINE:   found
21-10-2010 09:47:08 javax.xml.bind.ContextFinder safeLoadClass
FINE: Trying to load org.eclipse.persistence.jaxb.JAXBContextFactory
21-10-2010 09:47:08 javax.xml.bind.ContextFinder newInstance
FINE: loaded org.eclipse.persistence.jaxb.JAXBContextFactory from jar:file:/C:/Users/jpedersen22/.m2/repository/org/eclipse/persistence/eclipselink/2.1.1/eclipselink-2.1.1.jar!/org/eclipse/persistence/jaxb/JAXBContextFactory.class
UNMARSHALL: DeletableString[delete=true, value=010]
SimpleNavne[forNavn=null, fornavneMrkKode=]

ご覧のとおり、MOXy は使用中の JAXB 実装です。DeleteStringAdapter が使用されていない場合、@XMLPath アノテーションも使用して成功しました。つまり、getFornavn から @XmlJavaTypeAdapter(value = DeleteStringAdapter.class) を削除すると、次のようになります。

21-10-2010 10:00:02 javax.xml.bind.ContextFinder find
FINE: Trying to locate com/csc/cpr/ws/domain/jaxb.properties
21-10-2010 10:00:02 javax.xml.bind.ContextFinder loadJAXBProperties
FINE: loading props from file:/C:/Ajour/ajourworkspace/jaxb-test-with-schema/target/classes/com/csc/cpr/ws/domain/jaxb.properties
21-10-2010 10:00:02 javax.xml.bind.ContextFinder find
FINE:   found
21-10-2010 10:00:02 javax.xml.bind.ContextFinder safeLoadClass
FINE: Trying to load org.eclipse.persistence.jaxb.JAXBContextFactory
21-10-2010 10:00:02 javax.xml.bind.ContextFinder newInstance
FINE: loaded org.eclipse.persistence.jaxb.JAXBContextFactory from jar:file:/C:/Users/jpedersen22/.m2/repository/org/eclipse/persistence/eclipselink/2.1.1/eclipselink-2.1.1.jar!/org/eclipse/persistence/jaxb/JAXBContextFactory.class
SimpleNavne[forNavn=010, fornavneMrkKode=011]

XML 010 に関しては、さまざまなシナリオ (属性の有無にかかわらず) をテストするときに、私の人生を楽にするために作成されました。いずれにせよ、私の DeleteStringAdapter は削除属性を優先させます。これはまさに私が望んでいることです。

于 2010-10-21T08:02:34.713 に答える
1

これには XmlAdapter を使用できます。アダプターは、日付オブジェクトとの間で変換します。適合したオブジェクトには 2 つのプロパティがあります。@XmlValue で注釈が付けられた最初の日付プロパティと、@XmlAttribute で注釈が付けられたブール値のプロパティ。明日、完全な例を投稿します。

私のクラス

@XmlJavaTypeAdapter で startTime プロパティに注釈を付けます。

import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
class MyClass{

    private Date startTime;

    @XmlJavaTypeAdapter(DateAdapter.class)
    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

}

日付アダプター

DateAdapter クラスでは、Date のインスタンスと 2 つのプロパティ (日付とブール値) を持つクラスの間で変換を行います。

import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<AdaptedDate, Date> {

    @Override
    public AdaptedDate marshal(Date date) throws Exception {
        AdaptedDate adaptedDate = new AdaptedDate();
        adaptedDate.setDate(date);
        return adaptedDate;
    }

    @Override
    public Date unmarshal(AdaptedDate adaptedDate) throws Exception {
        return adaptedDate.getDate();
    }

}

適応日

これは、日付情報の 2 つのプロパティ表現です。XmlAdapter によって構築されます。

import java.util.Date;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;

public class AdaptedDate {

    private Date date;

    @XmlValue
    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @XmlAttribute
    public Boolean isDelete() {
        if(null == date) {
            return true;
        }
        return null;
    }

}

デモ

次のデモ コードを使用して、このコードを示すことができます。

import java.util.Date;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(MyClass.class);
        System.out.println(jc);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        MyClass myClass1 = new MyClass();
        marshaller.marshal(myClass1, System.out);

        MyClass myClass2 = new MyClass();
        myClass2.setStartTime(new Date());
        marshaller.marshal(myClass2, System.out);
    }

}

さらなる拡張:

  • @XmlElement(name="foo") アノテーションを追加することで、startTime 属性の要素名を制御できます。
  • @XmlPath() のような MOXy の @XmlPath("foo/bar") アノテーションを使用して、XML 表現をさらに制御できます。

詳細については、次を参照してください。

于 2010-10-19T21:59:45.047 に答える
0

私はあなたのプロジェクトを成功させました!また、プロジェクトを比較して、自分の問題を特定しました。DeletableString クラスを別のパッケージに配置しましたが、package-info.java ファイルを追加するのを忘れていました。以下の内容のファイルを追加すると、すべてが機能します。

@XmlSchema(namespace = "http://cpr.csc.com/navne", elementFormDefault = XmlNsForm.QUALIFIED)
package com.csc.cpr.ws.domain.customtypes;

import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

ご協力ありがとうございました。大変重宝しました

よろしく、ジェスパー

于 2010-10-21T14:53:20.320 に答える