QName 的创建采用默认命名空间前缀而不是提供的参数 [英] Creation of QName takes default namesapce prefix rather than the provided parameter

查看:50
本文介绍了QName 的创建采用默认命名空间前缀而不是提供的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 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 之后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 中的现有功能之上提供了各种额外的好处.发布此答案,因为它可能对将来的某人有所帮助:

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

  2. 由于您使用的是 Moxy,您可以使用所有必需的 NamespcaeURIPrefix 创建一个 Map.像这样:

 MapurisToPrefixes = new HashMap();urisToPrefixes.put("http://www.root.com", "rootNS");urisToPrefixes.put("http://sub.root.com", "subNS");

  1. 在使用编组方法时添加属性并将此 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:

  1. 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).

  2. Since you are using the Moxy you can create a Map with all of the required NamespcaeURI and Prefix. 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"); 

  1. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆