@XmlJavaTypeAdapterを使用して、タイプPerson
のオブジェクトを個人のUUIDのみを含むタイプPersonRef
のオブジェクトに置き換えるJAXBセットアップがあります。これは完全に正常に機能します。ただし、生成されたXMLは同じ名前空間を再宣言します(xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
)使用するたびに。これは一般的には問題ありませんが、正しく感じられません。
ドキュメントの冒頭でxmlns:xsiを宣言するようにJAXBを構成するにはどうすればよいですか?名前空間宣言をルート要素に手動で追加できますか?
これが私が達成したいことの例です:
現在:
<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a">
<relation type="CHILD">
<to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</relation>
<relation type="CHILD">
<to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</relation>
<!-- SNIP: some more relations -->
</person>
ウォンテッド:
<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<relation type="CHILD">
<to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/>
</relation>
<relation type="CHILD">
<to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/>
</relation>
<!-- SNIP: some more relations -->
</person>
あなたはコードでそれを行うことができます:
marshaller.setProperty("com.Sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
@Override
public String[] getPreDeclaredNamespaceUris() {
return new String[] {
XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
};
}
@Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI))
return "xsi";
if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI))
return "xs";
if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI))
return "xmime";
return suggestion;
}
});
thatきれいではありませんが、ルート要素に空のschemaLocationを追加できます。
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "");
JAXBカスタマイズ名前空間マッパーの問題 のように見えます
JAXB 1.0を使用してXMLドキュメントをマーシャリングすると、マーシャリングのプロセスを制御するJAXBオブジェクトであるMarshallerオブジェクトが、結果のXMLドキュメントに名前空間宣言を提供します。マーシャラーは、冗長に見える多くの名前空間宣言を生成することがあります。次に例を示します。
_ <?xml version="1.0"?>
<root>
<ns1:element xmlns:ns1="urn:foo"> ... </ns1:element>
<ns2:element xmlns:ns2="urn:foo"> ... </ns2:element>
<ns3:element xmlns:ns3="urn:foo"> ... </ns3:element>
</root>
_
JAXB2.0はこの動作を変更します。 JAXB 2.0(またはそれ以降)を使用してXMLドキュメントをマーシャリングする場合、マーシャラーは静的に既知のすべての名前空間URI(Uniform Resource Identifiers)、つまりJAXBアノテーションで要素名または属性名として使用されるURIを宣言します。
JAXBは、XMLドキュメントの途中で追加の名前空間を宣言することもあります。たとえば、属性または要素の値として使用される修飾名(
QName
)に新しい名前空間URIが必要な場合、またはドキュメントオブジェクトモデル(コンテンツツリーのDOM)ノードには、新しい名前空間URIが必要です。この動作により、自動生成された名前空間プレフィックスを持つ多くの名前空間宣言を持つXMLドキュメントが生成される場合があります。問題は、ns1、ns2、ns3などの自動生成された名前空間プレフィックスがユーザーフレンドリーではないことです。これらは通常、マーシャリングされたXMLを理解するのに役立ちません。
幸い、JAXB 2.0(またはそれ以降)は、マーシャリングに役立つ名前空間プレフィックスを指定するために使用できる _
com.Sun.xml.bind.marshaller.NamespacePrefixMapper
_ という名前のサービスプロバイダーインターフェイス(SPI)を提供します。JAXBSampleプログラムが最初にXMLドキュメントをマーシャルするとき、
NamespacePrefixMapper
クラスを使用せずにマーシャルします。その結果、マーシャラーは名前空間プレフィックス(この場合はns2)を自動的に生成します。
_ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:JustAnElement xmlns:ns2="a">
<foo>true</foo>
</ns2:JustAnElement>
_
名前空間の繰り返しを回避する構成の例:
JAXBSample
プログラムによって実行される2番目のマーシャリングは、次のようにNamespacePrefixMapper
クラスを使用します。
_ NamespacePrefixMapper m = new PreferredMapper();
marshal(jc, e, m);
public static class PreferredMapper extends NamespacePrefixMapper {
@Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
return "mappedNamespace" + namespaceUri;
}
}
_
PreferredMapper
クラスのgetPreferredPrefix()
メソッドは、優先プレフィックスを返します。この場合は、マーシャリングされたXMLのルート要素で宣言されるmappedNamespacea
です。
_ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
<foo>true</foo>
</mappedNamespacea:JustAnElement>
_
mavenを使用している場合は、これをpomに追加するだけです。
<dependency>
<groupId>com.Sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
上記の例で定義されているように注釈を構成する場合は、PreferredMapperは必要ありません。 package-info.javeファイルがありますが、次のように構成されています。
@javax.xml.bind.annotation.XmlSchema(
namespace = "mylovelynamespace1",
xmlns = {
@javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"),
@javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.mylovelycompanyname.package;
これは私がウェブで見つけた最良の答えです。
JAXBElement
の宣言された型が値の型と一致しないため、xsi:type
宣言が作成されている可能性があります。
ObjectFactory
に正しいJAXBElement
のcreateメソッドがある場合は、それを使用する必要があります。これは、QName
とタイプ情報の両方を正しく入力する必要があるためです。それ以外の場合は、JAXBElement
の宣言された型(2番目のコンストラクターarg)をString.class
ではなくCommentType.Comment
に設定してみます(これがcommentTest
の型であると仮定します)。
所有者:cbrettin
名前空間は一度だけ書き込むことができます。 XMLStreamWriterのプロキシクラスとpackage-info.Javaが必要になります。次に、コードで次のことを行います。
StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
.newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());
プロキシクラス(重要なメソッドは「writeNamespace」です):
class WrapperXMLStreamWriter implements XMLStreamWriter {
private final XMLStreamWriter writer;
public WrapperXMLStreamWriter(XMLStreamWriter writer) {
this.writer = writer;
}
//keeps track of what namespaces were used so that not to
//write them more than once
private List<String> namespaces = new ArrayList<String>();
public void init(){
namespaces.clear();
}
public void writeStartElement(String localName) throws XMLStreamException {
init();
writer.writeStartElement(localName);
}
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
init();
writer.writeStartElement(namespaceURI, localName);
}
public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
init();
writer.writeStartElement(prefix, localName, namespaceURI);
}
public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
if(namespaces.contains(namespaceURI)){
return;
}
namespaces.add(namespaceURI);
writer.writeNamespace(prefix, namespaceURI);
}
// .. other delegation method, always the same pattern: writer.method() ...
}
package-info.Java:
@XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED ,
xmlns = {
@XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package your.package;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
次のようにして、nsPrefix
マッピングを追加します。
marshaller.setNamespaceMapping("myns","urn:foo");
これはXMLであるため、DOMまたはXSLTを使用して出力を処理し、複数の名前空間参照を取り除くことができます。