QName 的创建采用默认命名空间前缀而不是提供的参数

Creation of QName takes default namesapce prefix rather than the provided parameter

我正在使用 JaxB Marshalling 创建 XML。我的 XML 我有几个使用 JAXB XmlAdapter 选项创建的自定义字段。自定义字段是使用 JAXBElement 创建的,其中 QName 是参数之一。

根据 QName 文档,它需要 3 个参数 NamespaceURILocalPartPrefix。我正在传递所有这些参数。但出于某种原因,创建的 XML 采用默认名称空间前缀 ns0, ns1, ns2 等,而不是 QName 创建参数中提供的前缀。

一切都按预期工作,没有任何问题。我只想知道如何让 QName 占用我作为参数传递的 custom prefix 值,而不是它自动添加的 default namespace prefix。我知道如果我不传递 prefix 值那么它将采用 default namespace prefix 但在我的情况下,即使在传递 custom prefix 值之后,它也会分配 default namespaces prefix到我想避免的 XML 。我尝试了很多方法,但仍然没有成功。

注意:我没有使用 javax Jaxb 库,而是使用基于 Jaxb 实现的 EclipseLink Moxy

目前,创建的 XML 看起来像这样:(请注意,这些不是根元素或 XML header 而是从某个部分获取的块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>

根据我提供给 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 class,它正在创建 QName。根据它创建 QName 的值类型,我有一个 Map<String, Object>。除了 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();
    }
}


以下是我在 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 -->

在尝试了很多东西并参考了很多东西之后我才能够得到它。我正在使用 Moxy 依赖项而不是 Jaxb,因为它在 Jaxb 中的现有功能之上提供了各种额外的好处。发布此答案,因为它对以后的人有帮助:

  1. 删除 package-info.java 及其所有内容(如果您在尝试某事时添加了它,因为我看到这里的很多答案都是基于它的)。

  2. 由于您使用的是 Moxy,因此您可以创建包含所有必需的 NamespcaeURIPrefix 的地图。像这样:

    Map<String, String> urisToPrefixes = new HashMap<String, String>();
    urisToPrefixes.put("http://www.root.com", "rootNS");
    urisToPrefixes.put("http://sub.root.com", "subNS"); 
  1. 在使用编组方法时添加 属性 并将此映射作为参数发送:
marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, urisToPrefixes);

这将确保每当遇到 Namespace 时,它都会检查相应的 prefix 并将其添加到 XML header 中,这样就可以了将所有默认前缀 ns0、ns1 等替换为映射中的相应前缀。

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);

如果您想阅读更多相关信息,请关注 official documentation here