JAXB에 의해 생성 된 @XmlRootElement가 없습니다.


209

FpML (Finanial Products Markup Language) 버전 4.5에서 Java 클래스를 생성하려고합니다. 많은 코드가 생성되었지만 사용할 수 없습니다. 간단한 문서를 직렬화하려고하면 다음과 같이됩니다.

javax.xml.bind.MarshalException
  - with linked exception: [com.sun.istack.SAXException2: unable
  to marshal type
  "org.fpml._2008.fpml_4_5.PositionReport"
  as an element because it is missing an
  @XmlRootElement annotation]

사실 classses는 @XmlRootElement 주석, 그래서 내가 잘못된 일을 할 수 없다? xjc (JAXB 2.1)를 fpml-main-4-5.xsd를 가리키고 있으며 모든 유형을 포함합니다.

답변:


261

JAXB XJC가 다른 사람들이 이미 언급하거나 암시 한 내용을 함께 묶기 @XmlRootElement위해 생성 된 클래스에 주석을 넣을지 여부를 결정하는 규칙 은 간단하지 않습니다 ( 이 기사 참조 ).

@XmlRootElementJAXB 런타임은 특정 오브젝트, 특히 XML 요소 이름 및 네임 스페이스를 마샬링 / 마샬링 해제하기 위해 특정 정보가 필요하기 때문에 존재합니다. 당신은 단지 마샬 러에게 오래된 물건을 전달할 수 없습니다. @XmlRootElement이 정보를 제공합니다.

주석은 단지 편의상이지만 JAXB에서는 주석이 필요하지 않습니다. 다른 방법은 JAXBElement래퍼 객체 를 사용하는 것입니다. 래퍼 객체는와 동일한 정보를 제공 @XmlRootElement하지만 주석이 아닌 객체의 형태 로 제공 됩니다.

그러나 JAXBElement비즈니스 논리가 일반적으로하지 않는 XML 요소 이름과 네임 스페이스를 알아야하므로 오브젝트를 작성하기가 어려워집니다.

고맙게도 XJC가 클래스 모델을 생성 할 때라는 클래스도 생성합니다 ObjectFactory. 이것은 부분적으로 JAXB v1과의 호환성을 위해 존재하지만 XJC가 JAXBElement자신의 객체 주위에 래퍼 를 생성하는 생성 된 팩토리 메소드를 넣을 수있는 곳이기도 합니다. XML 이름과 네임 스페이스를 처리하므로 걱정할 필요가 없습니다. 필요한 ObjectFactory메소드 를 찾기 위해 메소드 를 살펴보고 (대규모 스키마의 경우 수백 개가있을 수 있음) 필요합니다.


15
특수 사례 솔루션 : 클래스 생성에 사용 된 xsd를 수정할 수있는 경우 : 이 답변에 제공된 링크를 읽은 후 내 솔루션은 클래스를 생성하는 데 사용되는 xsd 파일을 수정하는 것이 었습니다. 루트 요소의 정의를 별도로 정의 된 유형에 대한 참조를 사용하는 대신 인라인 정의. 이를 통해 JAXB는이 요소를 @XmlRootElement로 설정할 수 있습니다. 이는 루트 요소에 이전에 사용되었던 elementType으로는 불가능했습니다.
Arthur

2
그러나 <scowl> 루트 요소를 인라인 유형으로 변경하면 모든 클래스가 루트 유형의 내부 클래스가됩니다. 또한 루트 요소 유형이 루트 요소 자체 (스키마에서 분명히 허용됨) 뒤에 정의되어 있어도 JAXB는 여전히 @XmlRootElement로 주석을 달지 않습니다.
Pawel Veselov

10
new ObjectFactory().createPositionReport(positionReport)반환JAXBElement<PositionReport>
vikingsteve

17
생성 된 ObjectFactory 메소드가 인수를 랩핑하는 메소드를 작성하지 않으면 JXBElement어떻게됩니까? 필자의 경우 팩토리 메소드는 0 배열이며 new객체를 반환 합니다. (왜 일부 클래스에는 JAXBElement 래퍼 도우미가 제공되고 다른 클래스에는 그렇지 않습니까?)이 경우 래퍼를 직접 만들어야합니까?
Carl G

1
@CarlG 나는 같은 상황에 처해 있습니다. 수업에 XmlRootElement 나 JAXBElement가 나타나지 않습니다. 이 사건에 대한 해결책을 찾았습니까?
Mickael Marrache

68

이것은 이미 위에 링크 된 블로그 게시물의 맨 아래에 언급되어 있지만 이것은 나를위한 치료법처럼 작동합니다.

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(new JAXBElement<MyClass>(new QName("uri","local"), MyClass.class, myClassInstance), System.out);

나는 표시된 답변을 선호하지만 이것은 나에게도 효과적입니다.
Pedro Dusso

1
jc위의 스 니펫에는 무엇이 있습니까?
Arun

3
@ ArunRaj 그것은 JAXBContext 클래스입니다
Gurnard

51

위의 답변 중 하나에서 암시 된 것처럼 XSD에서 해당 유형이 명명 된 유형으로 정의 된 경우 루트 요소에 XMLRootElement를 얻을 수 없습니다. 명명 된 유형은 XSD의 다른 곳에서 사용될 수 있기 때문입니다. 익명 유형으로 시도하십시오.

<xsd:element name="myRootElement" type="MyRootElementType" />

<xsd:complexType name="MyRootElementType">
...
</xsd:complexType>

너도 아마:

<xsd:element name="myRootElement">
    <xsd:complexType>
    ...
    <xsd:complexType>
</xsd:element>

1
그것은 사실이 아닙니다. 내 유형은 익명이며 (루트 요소에 포함됨) XmlRootElement 주석이 생성되지 않습니다. 어떤 생각?
Mickael Marrache

38

비 정렬 화에는 @XmlRootElement가 필요하지 않습니다-Unmarshaller # unmarshall의 2 매개 변수 형식을 사용하는 경우.

따라서 대신하는 경우 :

UserType user = (UserType) unmarshaller.unmarshal(new StringReader(responseString));

해야 할 일 :

JAXBElement<UserType> userElement = unmarshaller.unmarshal(someSource, UserType.class);
UserType user = userElement.getValue();

후자의 코드는 UserType 클래스 수준에서 @XmlRootElement 주석이 필요하지 않습니다.


2
XmlRootElement가없는 객체를 마샬링하는 똑같이 우아한 방법에 대해 알고 있습니까? skaffman, Gurnard et al.
Chris

4
+1 완벽하게 작동합니다! 더 명확하게 하나의 편집 ... 귀하의 솔루션에서 'someSource'는 매우 모호한 용어입니다. 자세히 설명 : JAXBElement <TargetClazz> root = unmarshaller.unmarshal (new StreamSource (new File ( "some.xml")), TargetClazz.class);
초신성

4
'someSource'에 대한 추가 설명 :String pathname = "file.xml"; InputStream stream = new FileInputStream(pathname); JAXBContext jaxbContext = JAXBContext.newInstance(UserType.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader someSource = factory.createXMLEventReader(stream); JAXBElement<UserType> userElement = jaxbUnmarshaller.unmarshal(someSource, UserType.class); UserType user = userElement.getValue();
Steve

21

Joe의 답변 (Joe Jun 26 '09 at 17:26)이 나를 위해 그것을합니다. JAXBElement를 마샬링하는 경우 @XmlRootElement 주석이 없으면 아무 문제가 없습니다. 나를 혼란스럽게 한 것은 생성 된 ObjectFactory에는 2 개의 createMyRootElement 메소드가 있다는 것입니다. 첫 번째는 매개 변수를 사용하지 않고 래핑되지 않은 객체를 제공하고 두 번째는 래핑되지 않은 객체를 가져 와서 JAXBElement로 래핑하고 JAXBElement가 올바르게 작동하도록 정렬합니다. 여기에 대부분에서 cribbed 사용되는 기본 코드 I (코드는이 회신에서 제대로 포맷되지 않았습니다의 사과 그래서 만약 내가이 새로운 해요)의 링크 텍스트 :

ObjectFactory objFactory = new ObjectFactory();
MyRootElement root = objFactory.createMyRootElement();
...
// Set root properties
...
if (!writeDocument(objFactory.createMyRootElement(root), output)) {
    System.err.println("Failed to marshal XML document");
}
...

private boolean writeDocument(JAXBElement document, OutputStream output) {

  Class<?> clazz = document.getValue().getClass();
  try {
    JAXBContext context =
        JAXBContext.newInstance(clazz.getPackage().getName());
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.marshal(document, output);
    return true;

  } catch (JAXBException e) {
    e.printStackTrace(System.err);
    return false;
  }
}

1
내 ObjectFactory 클래스가 JAXBElement 인스턴스가 아닌 일반 인스턴스를 리턴하는 메소드 만 정의하는 경우가 있습니다.
Mickael Marrache

20

XSD에서 기본 유형에 대한 @XmlRootElement 클래스를 생성하는 방법 의 바인딩을 사용하여이 문제를 해결할 수 있습니다 . .

다음은 Maven의 예입니다

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <version>1.3.1</version>
            <executions>
                <execution>
                    <id>xjc</id>
                    <goals>
                        <goal>xjc</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <schemaDirectory>src/main/resources/xsd</schemaDirectory>
                <packageName>com.mycompany.schemas</packageName>
                <bindingFiles>bindings.xjb</bindingFiles>
                <extension>true</extension>
            </configuration>
        </plugin>

binding.xjb파일 내용 은 다음과 같습니다

<?xml version="1.0"?>
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc"
              jxb:extensionBindingPrefixes="xjc" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="path/to/myschema.xsd" node="/xs:schema">
        <jxb:globalBindings>
            <xjc:simple/>
        </jxb:globalBindings>
    </jxb:bindings>
</jxb:bindings>

3
실제로 binding.xjb 파일에서 <xjc : simple>을 사용하여 트릭을 수행했습니다. 마샬링 코드 또는 WSDL을 변경하지 않으려는 경우 멋진 솔루션입니다. xjc : simple은 컬렉션 getter (예 : getOrder 대신 getOrders)에 대해 다른 메소드 이름 (복수)을 생성합니다.
dvtoever

10

알다시피 답은 ObjectFactory ()를 사용하는 것입니다. 다음은 나를 위해 일한 코드 샘플입니다. :)

ObjectFactory myRootFactory = new ObjectFactory();

MyRootType myRootType = myRootFactory.createMyRootType();

try {

        File file = new File("./file.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(MyRoot.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        //output pretty printed
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        JABXElement<MyRootType> myRootElement = myRootFactory.createMyRoot(myRootType);

        jaxbMarshaller.marshal(myRootElement, file);
        jaxbMarshaller.marshal(myRootElement, System.out);

    } catch (JAXBException e) {
        e.printStackTrace();
    }

요점으로 ... 중첩 요소에 ObjectFactory의 JAXBElement <?> create ... () 메소드를 어떻게 사용합니까? 예 : <SOAP-ENV : Header> <wsse : Security> <wsse : UsernameToken> </ wsse : UsernameToken> </ wsse : Security> </ SOAP-ENV : Header> 다음과 같은 메시지가 나타납니다. "유형"UsernameTokenType "을 마샬링 할 수 없습니다. @XmlRootElement 주석이 없기 때문에 요소로 "
Angelina

6

우리에게도 효과가 없습니다. 그러나 우리는 SOME 배경을 추가하는 광범위하게 인용 된 기사를 찾았습니다 ... 다음 사람을 위해 여기에 링크하겠습니다 : http://weblogs.java.net/blog/kohsuke/archive/2006/03 /why_does_jaxb_p.html


이것은 나를 위해 잘 작동했습니다, 감사합니다. 또한이 과정에서 잘못된 JAXB 객체 (내 생각과 같은 루트가 아님)를 마샬링하고 있음을 알았습니다. JAXBElement를 생성하는 것을 잊었고 바인딩에서 얻은 ObjectFactory 클래스에서 반환 된 객체를 마샬링하려고했습니다. 이것은 기본적으로 문제를 모두 처리했습니다 (다른 사람이 같은 문제에 부딪 칠 경우).
Joe Bane 2016 년

1
404 : "java.net 사이트가 닫혀서 죄송합니다. 이전에 java.net에서 호스팅 된 대부분의 오픈 소스 프로젝트가 이전되었습니다."
Tristan


6

이틀 동안 으깬 후 문제에 대한 해결책을 찾았습니다. ObjectFactory 클래스를 사용 하여 @XmlRootElement 가없는 클래스에 대한 해결 방법을 사용할 수 있습니다 . ObjectFactory는 JAXBElement를 감싸는 메소드를 오버로드했습니다.

방법 : 1 은 객체의 간단한 생성을 수행합니다.

방법 : 2@JAXBElement로 객체를 래핑합니다 .

javax.xml.bind.MarshalException을 피하려면 항상 Method : 2 를 사용하십시오 . 링크 된 예외에서 @XmlRootElement 주석이 누락되었습니다.

아래의 샘플 코드를 찾으십시오

방법 1 : 객체를 간단하게 생성합니다.

public GetCountry createGetCountry() {
        return new GetCountry();
    }

방법 : 2@JAXBElement로 객체를 래핑합니다 .

 @XmlElementDecl(namespace = "my/name/space", name = "getCountry")
 public JAXBElement<GetCountry> createGetCountry(GetCountry value) {
        return new JAXBElement<GetCountry>(_GetCountry_QNAME, GetCountry.class, null, value);
    }

작업 코드 샘플 :

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
WebServiceTemplate springWSTemplate = context.getBean(WebServiceTemplate.class);

GetCountry request = new GetCountry();
request.setGuid("test_guid");

JAXBElement<GetCountryResponse> jaxbResponse = (JAXBElement<GetCountryResponse>)springWSTemplate .marshalSendAndReceive(new ObjectFactory().createGetCountry(request));

GetCountryResponse response = jaxbResponse.getValue();

꽤 오랫동안 알아 내려고 고심했던 스프링 웹 서비스 템플릿으로 코드 참조를 제공 주셔서 감사합니다!
RRR_J

5

이 문제에 대한 나의 경험으로 누군가에게 유레카를 줄 수 있습니다! moment .. 다음을 추가하겠습니다.

IntelliJ의 "인스턴스 문서에서 xsd 생성"메뉴 옵션을 사용하여 생성 한 xsd 파일을 사용할 때도이 문제가 발생했습니다.

이 도구의 모든 기본값을 수락하면 jaxb와 함께 사용할 때 xsd 파일이 생성되고 java 파일은 no로 생성됩니다 @XmlRootElement. 런타임에 마샬링하려고 할 때이 질문에서 설명한 것과 동일한 예외가 발생했습니다.

나는 IntellJ 도구로 돌아가서 "Desgin Type"드롭 다운의 기본 옵션을 보았습니다 (물론 이해하지 못했지만 정직하면 여전히 이해하지 못합니다).

대상 유형 :

"로컬 요소 / 글로벌 복합 유형"

나는 이것을 다음으로 바꿨다.

"로컬 요소 / 유형"

이제는 (실질적으로) 다른 xsd를 생성하여 @XmlRootElementjaxb와 함께 사용될 때 생성했습니다 . 나는 그것의 안팎을 이해한다고 말할 수는 없지만 그것은 나를 위해 일했습니다.



4

JAXBElement 랩퍼는 @XmlRootElementJAXB가 no 를 생성 하지 않는 경우에 작동합니다 . 이 래퍼는에서 ObjectFactory생성 한 클래스 에서 사용할 수 있습니다 maven-jaxb2-plugin. 예를 들어 :

     public class HelloWorldEndpoint {
        @PayloadRoot(namespace = NAMESPACE_URI, localPart = "person")
        @ResponsePayload
        public JAXBElement<Greeting> sayHello(@RequestPayload JAXBElement<Person> request) {

        Person person = request.getValue();

        String greeting = "Hello " + person.getFirstName() + " " + person.getLastName() + "!";

        Greeting greet = new Greeting();
        greet.setGreeting(greeting);

        ObjectFactory factory = new ObjectFactory();
        JAXBElement<Greeting> response = factory.createGreeting(greet);
        return response;
      }
 }

3

xsd를 이렇게 바꾸려고 했습니까?

<!-- create-logical-system -->
<xs:element name="methodCall">
  <xs:complexType>
    ...
  </xs:complexType>
</xs:element>

이것은 JDK 1.7u71에서 나를 위해 일했습니다. 최상위 요소는 xjc에 의해 @XmlRootElement가 지정됩니다. 처음에는 최상위 수준의 복잡한 유형 만 사용했습니다. JAXBElement로 랩핑해야하는 것은 평범한 추악한 일입니다.
Serge Merzliakov

1

이를 해결하려면 generateElementProperty를 false로 설정하여 wsimport로 컴파일하기 전에 xml 바인딩을 구성해야합니다.

     <jaxws:bindings wsdlLocation="LOCATION_OF_WSDL"
      xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
      xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" 
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
         <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
    <jaxws:bindings  node="wsdl:definitions/wsdl:types/xs:schema[@targetNamespace='NAMESPACE_OF_WSDL']">
      <jxb:globalBindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xjc:generateElementProperty>false</xjc:generateElementProperty> 
      </jxb:globalBindings>
  </jaxws:bindings>
</jaxws:bindings>

랩핑 태그는<jaxb:bindings> ... <jaxws:bindings> ... </jaxws:bindings> ... </jaxb:bindings>
aliopi

0

주제는 상당히 오래되었지만 여전히 엔터프라이즈 비즈니스 컨텍스트와 관련이 있습니다. 나중에 쉽게 업데이트하기 위해 xsd를 만지지 않도록 노력했습니다. 여기 내 해결책이 있습니다 ..

1. 대부분 xjc:simple충분하다

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    jxb:extensionBindingPrefixes="xjc">

    <jxb:globalBindings>
        <xjc:simple/> <!-- adds @XmlRootElement annotations -->
    </jxb:globalBindings>

</jxb:bindings>

주로 xsd 정의를 가져 오기 위해 XmlRootElements를 만듭니다.

2. jaxb2-maven-plugin처형을 나누십시오

xsd 당 실행 정의 대신 여러 xsd 정의에서 클래스를 생성하려고 시도하면 큰 차이가 있습니다.

따라서 여러 개의 정의가있는 경우 <source>나누기보다는 시도하십시오.

          <execution>
            <id>xjc-schema-1</id>
            <goals>
              <goal>xjc</goal>
            </goals>
            <configuration>
              <xjbSources>
                <xjbSource>src/main/resources/xsd/binding.xjb</xjbSource>
              </xjbSources>
              <sources>
                <source>src/main/resources/xsd/definition1/</source>
              </sources>
              <clearOutputDir>false</clearOutputDir>
            </configuration>
          </execution>

          <execution>
            <id>xjc-schema-2</id>
            <goals>
              <goal>xjc</goal>
            </goals>
            <configuration>
              <xjbSources>
                <xjbSource>src/main/resources/xsd/binding.xjb</xjbSource>
              </xjbSources>
              <sources>
                <source>src/main/resources/xsd/definition2/</source>
              </sources>
              <clearOutputDir>false</clearOutputDir>
            </configuration>
          </execution>

생성기는 하나의 클래스로 충분하다는 사실을 포착하지 못하므로 실행마다 사용자 정의 클래스를 작성합니다. 그리고 그것이 정확히 내가 필요한 것입니다.).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.