복붙노트

[SPRING] NamespacePrefixMapper를 사용하지 않고 Spring JAXB 네임 스페이스 정의

SPRING

NamespacePrefixMapper를 사용하지 않고 Spring JAXB 네임 스페이스 정의

[이해 진행으로 심하게 편집]

NamespacePrefixMapper의 확장을 사용하지 않고 Spring Jaxb2Marshaller가 네임 스페이스 접두어의 사용자 정의 집합을 사용하도록 할 수 있습니까 (아니면 적어도 스키마 파일 / 주석에 지정된 접두어를 존중해야합니까)?

아이디어는 다른 클래스에 "has a"관계가있는 클래스를 가지게되는데,이 클래스는 다른 네임 스페이스를 가진 속성을 포함합니다. 이것을 더 잘 설명하기 위해 JDK1.6.0_12를 사용하는 다음 프로젝트 개요를 고려하십시오. (최신으로 직장에서 손을 얻을 수 있습니다.) org.example.domain 패키지에 다음 항목이 있습니다.

Main.java:

package org.example.domain;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class Main {
  public static void main(String[] args) throws JAXBException {
    JAXBContext jc = JAXBContext.newInstance(RootElement.class);

    RootElement re = new RootElement();
    re.childElementWithXlink = new ChildElementWithXlink();

    Marshaller marshaller = jc.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.marshal(re, System.out);
  }

}

RootElement.java:

package org.example.domain;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(namespace = "www.example.org/abc", name="Root_Element")
public class RootElement {
  @XmlElement(namespace = "www.example.org/abc")
  public ChildElementWithXlink childElementWithXlink;

}

ChildElementWithXLink.java:

package org.example.domain;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;

@XmlRootElement(namespace="www.example.org/abc", name="Child_Element_With_XLink")
public class ChildElementWithXlink {
  @XmlAttribute(namespace = "http://www.w3.org/1999/xlink")
  @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI")
  private String href="http://www.example.org";

}

package-info.java:

@javax.xml.bind.annotation.XmlSchema(
    namespace = "http://www.example.org/abc",
    xmlns = {
          @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"),
          @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink")
            }, 
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
    package org.example.domain;

Main.main ()을 실행하면 다음과 같은 결과가 출력됩니다.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Root_Element xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="www.example.org/abc">
<ns2:childElementWithXlink ns1:href="http://www.example.org"/>
</ns2:Root_Element>

반면에 내가 원하는 것은 :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<abc:Root_Element xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:abc="www.example.org/abc">
<abc:childElementWithXlink xlink:href="http://www.example.org"/>
</abc:Root_Element>

이 부분이 작동하면 문제는 간단한 컨텍스트 구성을 사용하여 동일하게 제공되도록 Spring에서 Jaxb2Marshaller를 구성 (Spring 2.5.6, spring-oxm-tiger-1.5.6 Jaxb2Marshaller 제공)하고 marshal () 호출.

이 문제에 대한 지속적인 관심에 감사드립니다.

해결법

  1. ==============================

    1.[JAXB-RI 대안을 제공하기위한 일부 편집은이 게시물의 끝 부분에 있습니다]

    [JAXB-RI 대안을 제공하기위한 일부 편집은이 게시물의 끝 부분에 있습니다]

    많은 머리를 긁어 낸 후에 마침내 제 환경 (Windows XP의 JDK1.6.0_12 및 Mac Leopard의 JDK1.6.0_20)을 받아 들여야했습니다. 나는 NamespacePrefixMapper 인 악의에 의존하지 않고서는이 작업을 수행 할 수 없습니다. . 그것은 왜 악한가? 프로덕션 코드에서 내부 JVM 클래스에 의존해야하기 때문입니다. 이러한 클래스는 JVM과 코드 간의 신뢰할 수있는 인터페이스의 일부가 아닙니다. 즉, JVM의 업데이트간에 변경됩니다.

    제 의견으로는 Sun이이 문제를 다루어야하거나 더 깊은 지식을 가진 사람이이 답변에 추가 할 수 있습니다 - 제발하십시오!

    계속 전진 해. NamespacePrefixMapper는 JVM 외부에서 사용되지 않아야하기 때문에 javac의 표준 컴파일 경로 (ct.sym에서 제어되는 rt.jar의 하위 섹션)에는 포함되어 있지 않습니다. 즉, IDE에 따라 달라지는 코드는 IDE에서 정상적으로 컴파일되지만 명령 행 (예 : Maven 또는 Ant)에서는 실패합니다. 이 문제를 극복하려면 rt.jar 파일을 빌드에 명시 적으로 포함시켜야하며, 심지어 경로에 공백이 있으면 Windows에 문제가있는 것 같습니다.

    이 자리에 있으면 다음과 같은 문제가 발생하는 Maven 스 니펫이 있습니다.

    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>2.1.9</version>
      <scope>system</scope>
      <!-- Windows will not find rt.jar if it is in a path with spaces -->
      <systemPath>C:/temp/rt.jar</systemPath>
    </dependency>
    

    rt.jar에 이상한 곳으로 하드 코드 된 경로를주의하십시오. 대부분의 OS에서 작동하는 {java.home} /lib/rt.jar의 조합으로이 문제를 해결할 수 있지만 Windows 공간 문제로 인해 문제가 발생하지는 않습니다. 예, 프로필을 사용하고 그에 따라 활성화 할 수 있습니다 ...

    또는 Ant에서 다음을 수행 할 수 있습니다.

    <path id="jre.classpath">
      <pathelement location="${java.home}\lib" />
    </path>
    // Add paths for build.classpath and define {src},{target} as usual
    <target name="compile" depends="copy-resources">
      <mkdir dir="${target}/classes"/>
      <javac bootclasspathref="jre.classpath" includejavaruntime="yes" debug="on" srcdir="${src}" destdir="${target}/classes" includes="**/*">
        <classpath refid="build.classpath"/>
      </javac>
    </target>    
    

    그리고 Jaxb2Marshaller Spring 구성은 무엇입니까? 자, 여기 내 자신의 NamespacePrefixMapper로 완성됩니다 :

    봄:

    <!-- JAXB2 marshalling (domain objects annotated with JAXB2 meta data) -->
    <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
    <property name="contextPaths">
      <list>
        <value>org.example.domain</value>
      </list>
    </property>
    <property name="marshallerProperties">
      <map>
        <!-- Good for JDK1.6.0_6+, lose 'internal' for earlier releases - see why it's evil? -->
        <entry key="com.sun.xml.internal.bind.namespacePrefixMapper" value-ref="myCapabilitiesNamespacePrefixMapper"/>
        <entry key="jaxb.formatted.output"><value type="boolean">true</value></entry>
      </map>
    </property>
    </bean>
    
    <!-- Namespace mapping prefix (ns1->abc, ns2->xlink etc) -->
    <bean id="myNamespacePrefixMapper" class="org.example.MyNamespacePrefixMapper"/>
    

    다음 내 NamespacePrefixMapper 코드 :

    public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
    
      public String getPreferredPrefix(String namespaceUri,
                                   String suggestion,
                                   boolean requirePrefix) {
        if (requirePrefix) {
          if ("http://www.example.org/abc".equals(namespaceUri)) {
            return "abc";
          }
          if ("http://www.w3.org/1999/xlink".equals(namespaceUri)) {
            return "xlink";
          }
          return suggestion;
        } else {
          return "";
        }
      }
    }
    

    그럼 거기에 있습니다. 나는 이것이 누군가가 고통을 피하는 데 도움이되기를 바랍니다. 아, 그런데, 위의 사악한 접근법을 부두에서 사용하면 다음과 같은 예외가 발생할 수 있습니다.

    java.lang.IllegalAccessError : 클래스 sun.reflect.GeneratedConstructorAccessor23의 슈퍼 클래스 sun.reflect.ConstructorAccessorImpl에 액세스 할 수 없습니다.

    그래서 행운을 빌어 요. 단서 : 웹 서버의 bootclasspath에있는 rt.jar.

    [JAXB-RI (Reference Implementation) 접근법을 보여줄 수 있도록 추가 편집]

    코드에 JAXB-RI 라이브러리를 도입 할 수 있다면 다음과 같은 수정을 통해 동일한 효과를 얻을 수 있습니다.

    본관:

    // Add a new property that implies external access
    marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
    

    MyNamespacePrefixMapper :

    // Change the import to this
    import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
    

    / lib 폴더에서 JAXB-RI 다운로드 (라이센스 고리를 뛰어 넘은 후)에서 다음 JAR을 추가하십시오.

    jaxb-impl.jar
    

    Main.main ()을 실행하면 원하는 출력이 생성됩니다.

  2. ==============================

    2.(크게 편집 된 응답)

    (크게 편집 된 응답)

    귀하의 코드에서 문제가 일부 네임 스페이스 URI 불일치로 인한 것이라고 생각합니다. 때로는 "http://www.example.org/abc"및 다른 번 "www.example.org/abc"를 사용하고 있습니다. 다음과 같은 트릭을해야합니다 :

    Main.java

    package org.example.domain;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Marshaller;
    
    public class Main {
    
        public static void main(String[] args) throws JAXBException { 
            JAXBContext jc = JAXBContext.newInstance(RootElement.class); 
            System.out.println(jc);
    
            RootElement re = new RootElement(); 
            re.childElementWithXlink = new ChildElementWithXlink(); 
    
            Marshaller marshaller = jc.createMarshaller(); 
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
            marshaller.marshal(re, System.out); 
          } 
    }
    

    RootElement.java

    package org.example.domain; 
    
    import javax.xml.bind.annotation.XmlElement; 
    import javax.xml.bind.annotation.XmlRootElement; 
    
    @XmlRootElement(namespace="http://www.example.org/abc", name="Root_Element") 
    public class RootElement { 
      @XmlElement(namespace = "http://www.example.org/abc") 
      public ChildElementWithXlink childElementWithXlink; 
    
    }
    

    ChildElementWithXLink.java

    package org.example.domain;
    
    import javax.xml.bind.annotation.XmlAttribute; 
    import javax.xml.bind.annotation.XmlRootElement; 
    import javax.xml.bind.annotation.XmlSchemaType; 
    
    @XmlRootElement(namespace="http://www.example.org/abc", name="Child_Element_With_XLink") 
    public class ChildElementWithXlink { 
      @XmlAttribute(namespace = "http://www.w3.org/1999/xlink") 
      @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI") 
      private String href="http://www.example.org"; 
    
    } 
    

    package-info.java

    @javax.xml.bind.annotation.XmlSchema( 
        namespace = "http://www.example.org/abc", 
        xmlns = { 
              @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"), 
              @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink") 
                },  
        elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
        package org.example.domain; 
    

    이제 Main.main ()을 실행하면 다음과 같은 결과가 출력됩니다.

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <abc:Root_Element xmlns:abc="http://www.example.org/abc" xmlns:xlink="http://www.w3.org/1999/xlink">
        <abc:childElementWithXlink xlink:href="http://www.example.org"/>
    </abc:Root_Element>
    
  3. ==============================

    3.@Blaise :이 정보로 MOXy의 문서를 업데이트 할 수 있습니까?

    @Blaise :이 정보로 MOXy의 문서를 업데이트 할 수 있습니까?

    NamespacePrefixMapper를 사용하지 않고 Spring JAXB 네임 스페이스 정의

    네임 스페이스 접두사를 구성하는 방법은 여기에 설명되어 있지 않다고 생각합니다. 감사!

  4. ==============================

    4.JDK 7의 JAXB 구현은 네임 스페이스 접두어를 지원합니다. JDK 1.6.0_21을 사용해 보았습니다.

    JDK 7의 JAXB 구현은 네임 스페이스 접두어를 지원합니다. JDK 1.6.0_21을 사용해 보았습니다.

  5. from https://stackoverflow.com/questions/3289644/define-spring-jaxb-namespaces-without-using-namespaceprefixmapper by cc-by-sa and MIT license