QName 的创建采用默认命名空间前缀而不是提供的参数 [英] Creation of QName takes default namesapce prefix rather than the provided parameter
问题描述
我正在使用 JaxB Marshalling
来创建 XML
.我的 XML 我有几个使用 JAXB XmlAdapter
选项创建的自定义字段.自定义字段是使用 JAXBElement
创建的,其中 QName
是参数之一.
根据 QName
文档,它需要 3 个参数 NamespaceURI
、LocalPart
和 Prefix
.我正在传递所有这些参数.但出于某种原因,创建的 XML
使用默认的命名空间前缀 ns0, ns1, ns2
等,而不是 QName
创建参数中提供的前缀.
一切都按预期工作,没有任何问题.我只想知道如何让 QName
占用我作为参数传递的 custom prefix
值而不是 default namespace prefix
它会自动添加.我知道如果我不传递 prefix
值,那么它将采用 default namespace prefix
但在我的情况下,即使在传递 custom prefix
之后code> 值,它将 default namespaces prefix
分配给 XML
我想避免.我尝试了很多东西,但仍然没有奏效.
注意:我没有使用 javax Jaxb
库,而是使用基于 Jaxb
实现的 EclipseLink Moxy
.
目前,创建的 XML 看起来像这样:(请注意,这些不是根元素或 XML 标头,而是取自 XML 的某个部分).
<ns1:SubField1 xmlns:ns1=https://fb.com?xsd=fb.xsd"><ns2:Field1 xmlns:ns2=https://fb.com?xsd=fb.xsd">Value1</ns2:Field1></ns1:SubField1><ns3:SubField3 xmlns:ns3=https://google.com?xsd=google.xsd">SubValue3</ns3:SubField3></ns0:MainField><ns4:MainField2 xmlns:ns4=https://google.com?xsd=google.xsd">MainValue2</ns4:MainField2>
根据我提供给 QName
字段的值,我期望它看起来像这样:
<google:MainField xmlns:google=https://google.com?xsd=google.xsd"><fb:SubField1 xmlns:fb=https://fb.com?xsd=fb.xsd"><fb:Field1 xmlns:fb="https://fb.com?xsd=fb.xsd">Value1</fb:Field1></fb:SubField1><google:SubField3 xmlns:google="https://google.com?xsd=google.xsd">SubValue3</google:SubField3></google:MainField><google:MainField2 xmlns:google="https://google.com?xsd=google.xsd">MainValue2</google:MainField2>
以下是我的 Java
类,它正在创建 QName
.我有一个基于它创建 QName
的值类型的 Map
.除了 QName 命名空间前缀
的一部分外,一切都按预期工作.
import io.openepcis.service.jaxb.DefaultJsonSchemaNamespaceURIResolver;导入 jakarta.xml.bind.JAXBElement;导入 jakarta.xml.bind.annotation.XmlAnyElement;导入 jakarta.xml.bind.annotation.adapters.XmlAdapter;导入 java.util.ArrayList;导入 java.util.List;导入 java.util.Map;导入 java.util.Optional;导入 javax.xml.namespace.QName;导入 org.apache.commons.lang3.NotImplementedException;公共类 MapAdapter extends XmlAdapter>{DefaultJsonSchemaNamespaceURIResolver uriResolver = new DefaultJsonSchemaNamespaceURIResolver();@覆盖公共地图<字符串,对象>unmarshal(MapWrapper valueType) 抛出异常 {抛出新的 NotImplementedException();}@覆盖public MapWrapper marshal(Map extensions) 抛出异常 {如果(扩展 == 空){返回空;}MapWrapper 包装器 = new MapWrapper();列表元素 = new ArrayList();//遍历扩展MAPfor (Map.Entry 属性:extensions.entrySet()) {final 可选的<字符串>xmlNamespace = uriResolver.namespaceURIFromSchema(extension.getKey());String namespaceURI = xmlNamespace.get();String localPart = getLocalPart(property.getKey());字符串前缀 = getPrefix(property.getKey());字符串标签 = getCleanLabel(property.getKey());System.out.println("namespaceURI:"+namespaceURI+"localPart:"+localPart+"prefix:"+prefix);//如果值类型是MAP则递归循环if (property.getValue() instanceof Map) {element.add(new JAXBElement(new QName(namespaceURI, localPart, prefix), MapWrapper.class, marshal((Map) property.getValue()));} else if (property.getValue() instanceof String) {//如果Value类型为String则直接创建JAXBElementelement.add(new JAXBElement(new QName(namespaceURI, localPart, prefix), String.class, property.getValue().toString()));}}包装器元素 = 元素;返回包装器;}//格式化 XML 标签的完整标签公共静态字符串 getCleanLabel(字符串标签){label = label.replaceAll("[()]", "").replaceAll("[^\\w\\s]", "_").replaceAll("",_");退货标签;}//格式化XML标签的LocalPart(在:"之后)公共静态字符串 getLocalPart(String localPart) {localPart = localPart.substring(localPart.indexOf(":") + 1);返回本地部分;}//格式化XML标签的前缀(在:"之前)公共静态字符串getPrefix(字符串前缀){prefix = prefix.substring(0, prefix.indexOf(":"));返回前缀;}}类 MapWrapper {@XmlAnyElement列表元素;}类 DefaultJsonSchemaNamespaceURIResolver{私有静态 MapxmlNamespaces = new HashMap();静止的 {xmlNamespaces.put("google", "https://google.com");xmlNamespaces.put("fb", "https://fb.com");xmlNamespaces.put("insta", "https://instagram.com");}公共可选<字符串>namespaceURIFromSchema(String schemaURI) {如果(xmlNamespaces.containsKey(schemaURI)){返回 Optional.of(xmlNamespaces.get(schemaURI));}返回 Optional.empty();}}
以下是我在 Maven pom.xml
中使用的依赖项:
<依赖><groupId>jakarta.xml.bind</groupId><artifactId>jakarta.xml.bind-api</artifactId><version>3.0.1</version></依赖><依赖><groupId>org.eclipse.persistence</groupId><artifactId>eclipselink</artifactId><version>3.0.0</version></依赖><!-- JAXB 注释依赖项结束-->
在尝试了很多东西并参考了很多东西之后,我终于明白了.我正在使用 Moxy
依赖项而不是 Jaxb
,因为它在 Jaxb
中的现有功能之上提供了各种额外的好处.发布此答案,因为它可能对将来的某人有所帮助:
删除
package-info.java
及其所有内容(如果您在尝试某些东西时添加了它,因为我看到这里的很多答案都是基于它的).>由于您使用的是
Moxy
,您可以使用所有必需的NamespcaeURI
和Prefix
创建一个 Map.像这样:
MapurisToPrefixes = new HashMap();urisToPrefixes.put("http://www.root.com", "rootNS");urisToPrefixes.put("http://sub.root.com", "subNS");
- 在使用编组方法时添加属性并将此 Map 作为参数发送:
marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, urisToPrefixes);
这将确保每当遇到 Namespace
时,它都会检查相应的 prefix
并将其添加到 XML 标头中,这样它将替换所有默认值将 ns0、ns1 等前缀添加到映射中的相应前缀.
JAXBContext ctx = JAXBContext.newInstance(new Class[] { TestObject.class, SubObject.class });映射<字符串,字符串>urisToPrefixes = new HashMap();urisToPrefixes.put("http://www.root.com", "rootNS");urisToPrefixes.put("http://sub.root.com", "subNS");马歇尔 m = ctx.createMarshaller();m.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER,prefixesToUris);
如果您想阅读更多相关信息,请关注官方文档在这里.
I am using the JaxB Marshalling
to create the XML
. My XML I have few custom fields which I am creating using the JAXB XmlAdapter
option. The custom fields are created using the JAXBElement
, in which QName
is one of the parameters.
As per the QName
documentation it takes 3 parameters NamespaceURI
, LocalPart
and Prefix
. I am passing all these parameters. But for some reason, the created XML
takes the default namespace prefix ns0, ns1, ns2
etc rather than the provided one in the QName
creation parameter.
Everything is working as expected without any issue. I just want to know how can I make the QName
take up the custom prefix
value that I am passing as a parameter rather than the default namespace prefix
it's adding automatically. I am aware that if I do not pass the prefix
value then it would take the default namespace prefix
but in my case, even after passing the custom prefix
value, it's assigning the default namespaces prefix
to XML
which I want to avoid. I tried many things but still, nothing worked out.
Note: I am not using the javax Jaxb
libraray rather than that I am using the EclipseLink Moxy
which is based on Jaxb
implementation.
Currently, the created XML would look something like this: (Please note that these do not root elements or XML header rather these are chunks taken from a certain part of the XML).
<ns0:MainField xmlns:ns0="https://google.com?xsd=google.xsd">
<ns1:SubField1 xmlns:ns1="https://fb.com?xsd=fb.xsd">
<ns2:Field1 xmlns:ns2="https://fb.com?xsd=fb.xsd">Value1</ns2:Field1>
</ns1:SubField1>
<ns3:SubField3 xmlns:ns3="https://google.com?xsd=google.xsd">SubValue3</ns3:SubField3>
</ns0:MainField>
<ns4:MainField2 xmlns:ns4="https://google.com?xsd=google.xsd">MainValue2</ns4:MainField2>
Where as I am expecting it to look something like this based on the values I have provided to the QName
field:
<google:MainField xmlns:google="https://google.com?xsd=google.xsd">
<fb:SubField1 xmlns:fb="https://fb.com?xsd=fb.xsd">
<fb:Field1 xmlns:fb="https://fb.com?xsd=fb.xsd">Value1</fb:Field1>
</fb:SubField1>
<google:SubField3 xmlns:google="https://google.com?xsd=google.xsd">SubValue3</google:SubField3>
</google:MainField>
<google:MainField2 xmlns:google="https://google.com?xsd=google.xsd">MainValue2</google:MainField2>
Following is my Java
class which is creating the QName
. I have a Map<String, Object>
based on the type of value it's creating the QName
. Everything is working as expected except the part of the QName namespace prefix
.
import io.openepcis.service.jaxb.DefaultJsonSchemaNamespaceURIResolver;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.annotation.XmlAnyElement;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.xml.namespace.QName;
import org.apache.commons.lang3.NotImplementedException;
public class MapAdapter extends XmlAdapter<MapWrapper, Map<String, Object>> {
DefaultJsonSchemaNamespaceURIResolver uriResolver = new DefaultJsonSchemaNamespaceURIResolver();
@Override
public Map<String, Object> unmarshal(MapWrapper valueType) throws Exception {
throw new NotImplementedException();
}
@Override
public MapWrapper marshal(Map<String, Object> extensions) throws Exception {
if (extensions == null) {
return null;
}
MapWrapper wrapper = new MapWrapper();
List elements = new ArrayList();
//Loop through the Extensions MAP
for (Map.Entry<String, Object> property : extensions.entrySet()) {
final Optional<String> xmlNamespace = uriResolver.namespaceURIFromSchema(extension.getKey());
String namespaceURI = xmlNamespace.get();
String localPart = getLocalPart(property.getKey());
String prefix = getPrefix(property.getKey());
String label = getCleanLabel(property.getKey());
System.out.println(" namespaceURI : " + namespaceURI + " localPart : " + localPart + " prefix : " + prefix);
//If the Value type is MAP then recurse through the loop
if (property.getValue() instanceof Map) {
elements.add(new JAXBElement<MapWrapper>(new QName(namespaceURI, localPart, prefix), MapWrapper.class, marshal((Map) property.getValue())));
} else if (property.getValue() instanceof String) {
// If the Value type is String then directly create JAXBElement
elements.add(new JAXBElement<String>(new QName(namespaceURI, localPart, prefix), String.class, property.getValue().toString()));
}
}
wrapper.elements = elements;
return wrapper;
}
// Formats the complete Label for the XML tag
public static String getCleanLabel(String label) {
label = label.replaceAll("[()]", "").replaceAll("[^\\w\\s]", "_").replaceAll(" ", "_");
return label;
}
//Formats the LocalPart of the XML Tag (after ":")
public static String getLocalPart(String localPart) {
localPart = localPart.substring(localPart.indexOf(":") + 1);
return localPart;
}
//Formats the Prefix of the XML Tag (before ":")
public static String getPrefix(String prefix) {
prefix = prefix.substring(0, prefix.indexOf(":"));
return prefix;
}
}
class MapWrapper {
@XmlAnyElement
List elements;
}
class DefaultJsonSchemaNamespaceURIResolver{
private static Map<String, String> xmlNamespaces = new HashMap<String, String>();
static {
xmlNamespaces.put("google", "https://google.com");
xmlNamespaces.put("fb", "https://fb.com");
xmlNamespaces.put("insta", "https://instagram.com");
}
public Optional<String> namespaceURIFromSchema(String schemaURI) {
if (xmlNamespaces.containsKey(schemaURI)) {
return Optional.of(xmlNamespaces.get(schemaURI));
}
return Optional.empty();
}
}
Following are the dependencies I am using in my Maven pom.xml
:
<!-- JAXB Annotations Dependencies START -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>3.0.0</version>
</dependency>
<!-- JAXB Annotations Dependencies END -->
After trying a lot of things and referring to many things I was able to get it. I am using the Moxy
dependency instead of Jaxb
as it provides various additional benefits on top of the existing features in Jaxb
. Posting this answer as it can be helpful to someone in the future:
Remove the
package-info.java
and all of its content (if you have added it while trying something because I see a lot of answers here are based on it).Since you are using the
Moxy
you can create a Map with all of the requiredNamespcaeURI
andPrefix
. Something like this:
Map<String, String> urisToPrefixes = new HashMap<String, String>();
urisToPrefixes.put("http://www.root.com", "rootNS");
urisToPrefixes.put("http://sub.root.com", "subNS");
- While using the Marshalling approach add the property and send this Map as a parameter:
marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, urisToPrefixes);
This will ensure that whenever a Namespace
is encountered it would check for the respective prefix
and add it to the XML header so in this way it would replace all the default prefix ns0,ns1, etc to the corresponding prefix from the map.
JAXBContext ctx = JAXBContext.newInstance(new Class[] { TestObject.class, SubObject.class });
Map<String, String> urisToPrefixes = new HashMap<String, String>();
urisToPrefixes.put("http://www.root.com", "rootNS");
urisToPrefixes.put("http://sub.root.com", "subNS");
Marshaller m = ctx.createMarshaller();
m.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, prefixesToUris);
If you would like to read more about it then follow the official documentation here.
这篇关于QName 的创建采用默认命名空间前缀而不是提供的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!