Apache CXF Aegisデータバインディングの紹介
1概要
このチュートリアルでは、http://cxf.apache.org/docs/aegis-21.html[Aegis]データバインディング、つまりJavaオブジェクトとXMLスキーマで記述されたXML文書との間のマッピングが可能なサブシステムについて紹介します。 Aegisでは、プログラミング作業を最小限に抑えながら、マッピングプロセスを詳細に制御できます。
Aegisはhttp://cxf.apache.org/[Apache CXF]の一部ですが、このフレームワーク内でのみ使用されることを制限されていません。代わりに、このデータバインディングメカニズムはどこでも使用できるので、このチュートリアルでは独立したサブシステムとしての使用法に焦点を当てます。
2 Mavenの依存関係
Aegisデータバインディングをアクティブにするために必要な唯一の依存関係は次のとおりです。
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-databinding-aegis</artifactId>
<version>3.1.8</version>
</dependency>
このアーティファクトの最新バージョンはhttps://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.apache.cxf%22%20AND%20a%3A%22cxf-rt-にあります。 databinding-aegis%22[ここ]。
3型の定義
このセクションでは、Aegisを説明するために使用される3つのタイプの定義について説明します。
3.1.
コース
これは、この例では最も単純なクラスで、次のように定義されています。
public class Course {
private int id;
private String name;
private String instructor;
private Date enrolmentDate;
//standard getters and setters
}
3.2.
CourseRepo
CourseRepo
は、このモデルの最上位タイプです。カスタムアダプタなしではJAXBでは不可能な、Javaインタフェースの整列化がどれほど簡単かを示すために、クラスではなくインタフェースとして定義します。
public interface CourseRepo {
String getGreeting();
void setGreeting(String greeting);
Map<Integer, Course> getCourses();
void setCourses(Map<Integer, Course> courses);
void addCourse(Course course);
}
getCourses
メソッドは戻り型
Map
で宣言していることに注意してください。
これは、JAXBに対するAegisのもう1つの利点を表すためのものです。前者はカスタムアダプタなしでマップをマーシャリングすることはできません。
3.3.
CourseRepoImpl
このクラスは
CourseRepo
インターフェースへの実装を提供します。
public class CourseRepoImpl implements CourseRepo {
private String greeting;
private Map<Integer, Course> courses = new HashMap<>();
//standard getters and setters
@Override
public void addCourse(Course course) {
courses.put(course.getId(), course);
}
}
4カスタムデータバインディング
カスタマイズを有効にするには、XMLマッピングファイルがクラスパスに存在する必要があります。これらのファイルは、関連するJava型のパッケージ階層に対応する構造を持つディレクトリに配置する必要があります。
たとえば、完全修飾名クラスの名前が
package.ClassName
の場合、その関連マッピングファイルはクラスパスの
package/ClassName
サブディレクトリ内になければなりません。マッピングファイルの名前は、
.aegis.xml
サフィックスが追加された、関連付けられたJava型と同じである必要があります。
4.1.
CourseRepo
マッピング
CourseRepo
インターフェースは
com.baeldung.cxf.aegis
パッケージに属しているため、対応するマッピングファイルは
CourseRepo.aegis.xml
という名前でクラスパスの
com/baeldung/cxf/aegis
ディレクトリに配置されます。
CourseRepo
マッピングファイルでは、
CourseRepo
インタフェースに関連付けられているXML要素の名前と名前空間、およびその
greeting
プロパティのスタイルを変更します。
<mappings xmlns:ns="http://courserepo.baeldung.com">
<mapping name="ns:Baeldung">
<property name="greeting" style="attribute"/>
</mapping>
</mappings>
4.2. コースマッピング
CourseRepo
タイプと同様に、クラス
Course
のマッピングファイルは
Course.aegis.xml
という名前であり、
com/baeldung/cxf/aegis
ディレクトリにもあります。
このマッピングファイルでは、マーシャリング時に
Course
クラスの
instructor
プロパティを無視するようにAegisに指示しているので、その値は出力XMLドキュメントから再作成されたオブジェクトでは使用できません。
<mappings>
<mapping>
<property name="instructor" ignore="true"/>
</mapping>
</mappings>
Aegisのホームページ
は、私たちがより多くのカスタマイズオプションを見つけることができるところです。
5テスト中
このセクションは、Aegisデータバインディングの使用法を説明するテストケースを設定および実行するためのステップバイステップガイドです。
テストプロセスを容易にするために、テストクラス内に2つのフィールドを宣言します。
public class BaeldungTest {
private AegisContext context;
private String fileName = "baeldung.xml";
//other methods
}
これらのフィールドは、このクラスの他のメソッドで使用されるようにここで定義されています。
5.1.
AegisContext
初期化
まず、
AegisContext
オブジェクトを作成する必要があります。
context = new AegisContext();
その
AegisContext
インスタンスが設定され初期化されます。コンテキストにルートクラスを設定する方法は次のとおりです。
Set<Type> rootClasses = new HashSet<Type>();
rootClasses.add(CourseRepo.class);
context.setRootClasses(rootClasses);
Aegisは、
Set <Type>
オブジェクト内の各
Type
に対してXMLマッピング要素を作成します。このチュートリアルでは、
CourseRepo
のみをルートタイプとして設定します。
それでは、コンテキストの実装マップを設定して、
CourseRepo
インターフェースのプロキシクラスを指定しましょう。
Map<Class<?>, String> beanImplementationMap = new HashMap<>();
beanImplementationMap.put(CourseRepoImpl.class, "CourseRepo");
context.setBeanImplementationMap(beanImplementationMap);
Aegisコンテキストの最後の設定は、対応するXML文書に
xsi:type
属性を設定するように指示することです。この属性は、マッピングファイルで上書きされない限り、関連付けられているJavaオブジェクトの実際の型名を保持します。
context.setWriteXsiTypes(true);
AegisContext
インスタンスが初期化される準備が整いました。
context.initialize();
コードをきれいに保つために、このサブセクションのすべてのコードスニペットを1つのヘルパーメソッドにまとめます。
private void initializeContext() {
//...
}
5.2. 簡単なデータ設定
このチュートリアルは単純なので、永続的なソリューションに頼るのではなく、メモリ内でサンプルデータを生成します。以下のセットアップロジックを使用してコースリポジトリにデータを入力しましょう。
private CourseRepoImpl initCourseRepo() {
Course restCourse = new Course();
restCourse.setId(1);
restCourse.setName("REST with Spring");
restCourse.setInstructor("Eugen");
restCourse.setEnrolmentDate(new Date(1234567890000L));
Course securityCourse = new Course();
securityCourse.setId(2);
securityCourse.setName("Learn Spring Security");
securityCourse.setInstructor("Eugen");
securityCourse.setEnrolmentDate(new Date(1456789000000L));
CourseRepoImpl courseRepo = new CourseRepoImpl();
courseRepo.setGreeting("Welcome to Beldung!");
courseRepo.addCourse(restCourse);
courseRepo.addCourse(securityCourse);
return courseRepo;
}
5.3. JavaオブジェクトとXML要素のバインド
JavaオブジェクトをXML要素に整列化するために必要な手順は、次のヘルパーメソッドで説明されています。
private void marshalCourseRepo(CourseRepo courseRepo) throws Exception {
AegisWriter<XMLStreamWriter> writer = context.createXMLStreamWriter();
AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class);
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance()
.createXMLStreamWriter(new FileOutputStream(fileName));
writer.write(courseRepo,
new QName("http://aegis.cxf.baeldung.com", "baeldung"), false, xmlWriter, aegisType);
xmlWriter.close();
}
ご覧のとおり、
AegisWriter
および
AegisType
オブジェクトは
AegisContext
インスタンスから作成する必要があります。
AegisWriter
オブジェクトは、指定されたJavaインスタンスを指定された出力に整列化します。
この場合、これはファイルシステムの
fileName
クラスレベルフィールドの値にちなんで名付けられたファイルに関連付けられた
XMLStreamWriter
オブジェクトです。
次のメソッドは、XMLドキュメントを特定の型のJavaオブジェクトに非整列化します。
private CourseRepo unmarshalCourseRepo() throws Exception {
AegisReader<XMLStreamReader> reader = context.createXMLStreamReader();
XMLStreamReader xmlReader = XMLInputFactory.newInstance()
.createXMLStreamReader(new FileInputStream(fileName));
CourseRepo courseRepo = (CourseRepo) reader.read(
xmlReader, context.getTypeMapping().getType(CourseRepo.class));
xmlReader.close();
return courseRepo;
}
ここでは、
AegisRetext
オブジェクトが
AegisContext
インスタンスから生成されています。
AegisReader
オブジェクトは、提供された入力からJavaオブジェクトを作成します。この例では、その入力は、上記の
marshalCourseRepo
メソッドで生成したファイルを基にした
XMLStreamReader
オブジェクトです。
5.4. アサーション
それでは、前のサブセクションで定義したすべてのヘルパーメソッドをテストメソッドにまとめましょう。
@Test
public void whenMarshalingAndUnmarshalingCourseRepo__thenCorrect()
throws Exception {
initializeContext();
CourseRepo inputRepo = initCourseRepo();
marshalCourseRepo(inputRepo);
CourseRepo outputRepo = unmarshalCourseRepo();
Course restCourse = outputRepo.getCourses().get(1);
Course securityCourse = outputRepo.getCourses().get(2);
//JUnit assertions
}
最初に
CourseRepo
インスタンスを作成し、次にそれをXML文書に整列化し、最後にその文書を非整列化して元のオブジェクトを再作成します。再作成したオブジェクトが期待どおりであることを確認しましょう。
assertEquals("Welcome to Beldung!", outputRepo.getGreeting());
assertEquals("REST with Spring", restCourse.getName());
assertEquals(new Date(1234567890000L), restCourse.getEnrolmentDate());
assertNull(restCourse.getInstructor());
assertEquals("Learn Spring Security", securityCourse.getName());
assertEquals(new Date(1456789000000L), securityCourse.getEnrolmentDate());
assertNull(securityCourse.getInstructor());
instructor
プロパティを除いて、
Date
型の値を持つ
enrolmentDate
プロパティも含めて、他のすべての値で値が復元されることは明らかです。
Course
オブジェクトを整列化するときにAinsが
instructor
プロパティを無視するように指示したので、これはまさに私たちが期待していることです。
5.5. 出力XMLドキュメント
Aegisマッピングファイルの効果を明確にするために、カスタマイズなしのXML文書を以下に示します。
<ns1:baeldung xmlns:ns1="http://aegis.cxf.baeldung.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns1:CourseRepo">
<ns1:courses>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>1</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2009-02-14T06:31:30+07:00
</ns1:enrolmentDate>
<ns1:id>1</ns1:id>
<ns1:instructor>Eugen</ns1:instructor>
<ns1:name>REST with Spring</ns1:name>
</ns2:value>
</ns2:entry>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>2</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2016-03-01T06:36:40+07:00
</ns1:enrolmentDate>
<ns1:id>2</ns1:id>
<ns1:instructor>Eugen</ns1:instructor>
<ns1:name>Learn Spring Security</ns1:name>
</ns2:value>
</ns2:entry>
</ns1:courses>
<ns1:greeting>Welcome to Beldung!</ns1:greeting>
</ns1:baeldung>
これをAegisカスタムマッピングが動作している場合と比較してください。
<ns1:baeldung xmlns:ns1="http://aegis.cxf.baeldung.com"
xmlns:ns="http://courserepo.baeldung.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns:Baeldung" greeting="Welcome to Beldung!">
<ns:courses>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>1</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2009-02-14T06:31:30+07:00
</ns1:enrolmentDate>
<ns1:id>1</ns1:id>
<ns1:name>REST with Spring</ns1:name>
</ns2:value>
</ns2:entry>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>2</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2016-03-01T06:36:40+07:00
</ns1:enrolmentDate>
<ns1:id>2</ns1:id>
<ns1:name>Learn Spring Security</ns1:name>
</ns2:value>
</ns2:entry>
</ns:courses>
</ns1:baeldung>
このセクションで定義したテストを実行した後、このXML構造はプロジェクトのメインディレクトリのすぐ下の
baeldung.xml
にあります。
CourseRepo
オブジェクトに対応するXML要素の
type
属性と名前空間が、
CourseRepo.aegis.xml
ファイルに設定した内容に従って変わることがわかります。
greeting
プロパティも属性に変換され、
Course
オブジェクトの
instructor
プロパティは予想通りに消えます。
デフォルトでは、Aegisは基本的なJavaの型を最もよく一致するスキーマの型に変換します。このチュートリアルに示すように、
Date
オブジェクトから
xsd:dateTime
要素まで。ただし、対応するマッピングファイルに設定を設定することで、その特定のバインディングを変更できます。
詳細については、http://cxf.apache.org/docs/aegis-21.html[Aegis home page]を参照してください。
6. 結論
このチュートリアルでは、Apache CXF Aegisデータバインディングをスタンドアロンサブシステムとして使用する方法を説明します。 Aegisを使用してJavaオブジェクトをXML要素にマッピングする方法、およびその逆の方法を示します。
このチュートリアルでは、データバインディング動作をカスタマイズする方法にも焦点を当てています。
そして、いつものように、これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/apache-cxf[the GitHub project]にあります。