JAXB tarafından @XmlRootElement oluşturulmadı


209

FpML (Finanial Products Markup Language) sürüm 4.5 Java sınıfları oluşturmak çalışıyorum. Bir ton kod üretildi, ancak kullanamıyorum. Basit bir belgeyi serileştirmeye çalışıyorum:

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]

Aslında hiçbir sınıfta @XmlRootElement ek açıklaması yoktur, bu yüzden ne yanlış yapabilirim? Ben tüm türleri içeren fpml-main-4-5.xsd, xjc (JAXB 2.1) işaret ediyorum.

Yanıtlar:


261

Diğerlerinin daha önce belirttikleri veya ima ettikleri şeyleri bir araya getirmek için, JAXB XJC'nin @XmlRootElementoluşturulmuş bir sınıfa ek açıklama koyup koymayacağına karar vermesi gereken kurallar önemsiz değildir ( bu makaleye bakın ).

@XmlRootElementçünkü JAXB çalışma zamanı belirli bir nesneyi, özellikle XML öğesi adını ve ad alanını marşal / marşsiz hale getirmek için belirli bilgiler gerektirir. Eski bir nesneyi Marshaller'a geçiremezsiniz.@XmlRootElementbu bilgileri sağlar.

Bununla birlikte, açıklama sadece bir kolaylıktır - JAXB gerektirmez. Alternatifi JAXBElement, aynı bilgileri sağlayan sarıcı nesneleri kullanmaktır .@XmlRootElement , ek açıklama yerine ancak nesne biçiminde .

Ancak, JAXBElement iş mantığının genellikle yapmadığı XML öğesi adını ve ad alanını bilmeniz gerektiğinden nesneler oluşturmak gariptir.

Neyse ki, XJC bir sınıf modeli oluşturduğunda, aynı zamanda adlı bir sınıf oluşturur ObjectFactory. Bu kısmen JAXB v1 ile geriye dönük uyumluluk için var, ancak XJC'nin JAXBElementkendi nesnelerinizin etrafına sarmalayıcılar oluşturan oluşturulan fabrika yöntemlerini koyması için bir yer olarak da var . XML adını ve ad alanını sizin için işler, böylece endişelenmenize gerek yoktur. İhtiyacınız ObjectFactoryolanı bulmak için yöntemleri (ve büyük şema için yüzlerce olabilir) incelemeniz yeterlidir.


15
Özel durum çözümü: ne zaman sınıf üretimi için kullanılan xsd değiştirebilirsiniz: Bu cevap verilen bağlantıyı okuduktan sonra benim durumumda çözüm sınıfları oluşturmak için kullanılan xsd dosyasını değiştirmek oldu: Ben kök eleman tanımını bir ayrı olarak tanımlanan bir türe başvuruyu kullanmak yerine satır içi tanım. Bunlar, JAXB'nin bu öğeyi kök öğesi için daha önce kullanılan elementType ile mümkün olmayan @XmlRootElement olarak ayarlamasına izin verir.
Arthur

2
<scowl> kök elemanını satır içi tür olacak şekilde değiştirmek, tüm sınıfların kök türünün iç sınıfları olmasını sağlar. Ayrıca, kök öğe türü kök öğenin kendisinden SONRA tanımlanmış olsa bile (görünüşte şema tarafından izin verilir), JAXB @XmlRootElement ile açıklama eklemeyecektir.
Pawel Veselov

10
ie new ObjectFactory().createPositionReport(positionReport)dönerJAXBElement<PositionReport>
vikingsteve

17
Oluşturulan ObjectFactory yöntemi, bağımsız değişkeni bir JXBElement. Benim durumumda, fabrika yöntemi 0-arity ve sadece bir newnesne döndürür . (Neden bazı sınıflara JAXBElement sarıcı yardımcıları verilir ve diğerleri verilmiyor?) Sanırım bu durumda sargıyı kendimiz oluşturmalıyız?
Carl G

1
@CarlG Aynı durumdayım - derslerimde XmlRootElement veya JAXBElement görünmüyor. Bu dava için bir çözüm buldunuz mu?
Mickael Marrache

68

Bu, yukarıda zaten bağlı olan blog gönderisinin alt kısmında belirtilmiştir, ancak bu benim için bir tedavi gibi çalışır:

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

İşaretli cevabı tercih ederim, ama bu benim için de geçerli.
Pedro Dusso

1
jcyukarıdaki kod parçasında ne var ?
Arun

3
@ArunRaj JAXBContext sınıfı
Gurnard

51

Yukarıdaki yanıtlardan birinde belirtildiği gibi, XSD'de türü adlandırılmış bir tür olarak tanımlandıysa, kök öğenizde bir XMLRootElement elde edemezsiniz, çünkü bu adlandırılmış tür XSD'nizin başka bir yerinde kullanılabilir. Anonim bir tür mking yapmayı deneyin, örneğin:

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

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

olurdu:

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

1
Bu benim için doğru değil. Türüm anonim (kök öğeme gömülü) ve XmlRootElement ek açıklaması oluşturulmadı. Herhangi bir fikir?
Mickael Marrache

38

@XmlRootElement, e-posta paylaşımı için gerekli değildir - Unmarshaller # unmarshall öğesinin 2 parametre biçimini kullanırsa.

Yani, yapmak yerine:

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

biri yapmalı:

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

İkinci kod, UserType sınıf düzeyinde @XmlRootElement ek açıklaması gerektirmez.


2
XmlRootElement olmayan bir nesneyi skaffman, Gurnard ve ark tarafından belirtildiği gibi bir JAXBElement'e sarmadan aynı derecede zarif bir yol biliyor musunuz?
Chris

4
+1 Mükemmel çalışıyor! Daha fazla netlik için bir düzenleme ... Çözümünüzde 'someSource' çok belirsiz bir terimdir. Ayrıntılı olarak açıklamak için: JAXBElement <TargetClazz> root = unmarshaller.unmarshal (yeni StreamSource (yeni Dosya ("some.xml")), TargetClazz.class);
süpernova

4
'SomeSource' hakkında daha fazla ayrıntı: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 Pitchers

21

Joe'nun cevabı (Joe Jun 26 '09, 17:26) bunu benim için yapar. Basit cevap, bir JAXBElement'i marshal yaparsanız @XmlRootElement ek açıklamasının olmaması sorun değildir. Beni karıştıran şey, üretilen ObjectFactory'nin 2 createMyRootElement yöntemine sahip olmasıdır - birincisi parametre almaz ve paketlenmemiş nesneyi verir, ikincisi paketlenmemiş nesneyi alır ve bir JAXBElement içinde sarılmış olarak döndürür ve JAXBElement'in düzgün çalıştığını sıralar. İşte kullandığım temel kod (bu konuda yeniyim, bu yüzden kod bu cevapta doğru biçimlendirilmemişse özür dilerim), büyük ölçüde bağlantı metninden cribed :

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

2
ObjectFactory sınıfımın JAXBElement örneklerini değil, yalnızca normal örnekleri döndüren yöntemleri tanımladığı bir durum var ...
Mickael Marrache

20

Sen den bağlama kullanarak bu sorunu çözebilirsiniz XSD Baz Türleri için @XmlRootElement Sınıfları nasıl oluşturulur?.

İşte Maven ile bir örnek

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

İşte binding.xjbdosya içeriği

<?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
Gerçekten, ciltleme.xjb dosyasında <xjc: simple> kullanmak hile yaptı. Seçim kodunuzu veya WSDL'nizi değiştirmek istemiyorsanız harika bir çözüm. Xjc: simple uygulamasının toplama alıcıları için farklı yöntem adları (çoğul) oluşturduğunu unutmayın (örneğin getOrder yerine getOrders)
dvtoever

10

Bildiğiniz gibi cevap ObjectFactory () kullanmaktır. İşte benim için çalışan kodun bir örneği :)

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

noktaya ... iç içe öğeler için ObjectFactory JAXBElement <?> create ... () yöntemleri nasıl kullanabilirim? ie: <SOAP-ENV: Üstbilgi> <wsse: Güvenlik> <wsse: UsernameToken> </ wsse: UsernameToken> </ wsse: Security> </ SOAP-ENV: Header> "" Kullanıcı AdıTokenType "marshal yazamıyorum" bir element olarak çünkü bir @XmlRootElement ek açıklaması eksik "
Angelina

6

Bizim için de çalışmıyor. Ancak, BAZI arka plan ekleyen ve alıntı yapılan bir makale bulduk ... Bir sonraki kişi için buraya bağlantı vereceğim: http://weblogs.java.net/blog/kohsuke/archive/2006/03 /why_does_jaxb_p.html


Bu benim için iyi çalıştı, teşekkürler. Ben de bu süreçte yanlış JAXB nesnesi (düşündüğüm gibi kök) marshaling olduğunu bulundu. Bir JAXBElement oluşturmayı unuttum ve bağlamadan elde ettiğim ObjectFactory sınıfından sadece döndürülen nesneyi marshal etmeye çalışıyordum. Bu temelde sorunu tamamen ele aldı (başka birinin aynı problemle karşılaşması durumunda).
Joe Bane

1
404: "Üzgünüz, java.net sitesi kapatıldı. Daha önce java.net'te barındırılan birçok Açık Kaynak projesinin yeri değiştirildi."
Tristan


6

İki gün boyunca uyuşturucu aldıktan sonra sorunun çözümünü buldum . @XmlRootElement olmayan sınıflar için geçici çözüm bulmak için ObjectFactory sınıfını kullanabilirsiniz . ObjectFactory, JAXBElement çevresine sarmak için aşırı yüklenmiş yöntemlere sahiptir.

Yöntem: 1 nesnenin basit oluşturulmasını yapar.

Yöntem: 2 nesneyi @JAXBElement ile sarar .

Javax.xml.bind.MarshalException öğesinden kaçınmak için her zaman Yöntem: 2'yi kullanın.

Lütfen aşağıdaki örnek kodu bulun

Yöntem: 1 nesnenin basit oluşturulmasını yapar

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

Yöntem: 2 nesneyi @JAXBElement ile sarar .

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

Çalışma kodu örneği:

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

Bahar webservice şablonu ile kod referansı verdiğiniz için teşekkürler, bir süredir anlamaya çalışıyoruz!
RRR_J

5

Bu sorunla ilgili deneyimim birisine Eureka verir! an .. Aşağıdakileri ekleyeceğim:

IntelliJ'in "Instance belgesinden xsd oluştur" menü seçeneğini kullanarak oluşturduğum bir xsd dosyasını kullanırken de bu sorunu alıyordum.

Bu aracın tüm varsayılanlarını kabul ettiğimde, jaxb ile kullanıldığında java dosyaları oluşturmayan bir xsd dosyası oluşturdu. @XmlRootElement . Mareşal yapmaya çalıştığımda çalışma zamanında bu soruda tartışılan istisnayı aldım.

IntellJ aracına geri döndüm ve "Desgin Type" açılır menüsünde varsayılan seçeneği gördüm (ki tabii ki anlamadım .. ve hala dürüst olsam da yok):

Desgin Türü:

"yerel öğeler / Küresel karmaşık türler"

Bunu ben olarak değiştirdim

"yerel öğeler / türler"

, şimdi @XmlRootElementjaxb ile kullanıldığında üretilen (büyük ölçüde) farklı bir xsd üretti . İçini ve dışını anladığımı söyleyemem, ama benim için çalıştı.



4

JAXBElement sarmalayıcıları @XmlRootElement, JAXB tarafından hiç üretilmeyen durumlarda çalışır . Bu sarmalayıcılar ObjectFactorytarafından oluşturulan sınıfta mevcuttur maven-jaxb2-plugin. Örneğin:

     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'nizi böyle değiştirmeye çalıştınız mı?

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

Bu benim için JDK 1.7u71 ile çalıştı. Üst düzey bir öğeye @xmlRootElement öğesi xjc tarafından atanır. Başlangıçta sadece üst düzey bir karmaşık tip vardı. Bir JAXBElement sarmak zorunda çirkin.
Serge Merzliakov

1

Bunu çözmek için, wsimport ile derlemeden önce bir xml bağlayıcısı yapılandırmanız gerekir.

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

sarma etiketi olmalıdır<jaxb:bindings> ... <jaxws:bindings> ... </jaxws:bindings> ... </jaxb:bindings>
aliopi

0

Konu oldukça eski ama yine de kurumsal iş bağlamında geçerli. Gelecekte kolayca güncellemek için xsds'e dokunmaktan kaçınmaya çalıştım. İşte çözümlerim ..

1. Çoğunlukla xjc:simpleyeterlidir

<?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>

Çoğunlukla xsd tanımlarını içe aktarmak için XmlRootElements oluşturur.

2. jaxb2-maven-pluginİnfazlarınızı bölün

Xsd başına bir yürütme tanımı yerine birden çok xsd tanımlarından sınıflar oluşturmaya çalışırsanız çok büyük bir farkla karşılaştım.

Eğer birden fazla tanım içeren bir tanımınız varsa <source>, bunları ayırmaya çalışın:

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

Üretici, bir sınıfın yeterli olabileceği gerçeğini yakalamaz ve bu nedenle yürütme başına özel sınıflar oluşturur. Ve tam olarak ihtiyacım olan şey bu;).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.