문제

XML을 읽고 쓰기 위해 JAXB를 사용하고 있습니다. 내가 원하는 것은 마샬링을위한 기본 JAXB 클래스와 상속되지 않은 JAXB 클래스를 사용하는 것입니다. 이를 통해 발신자 Java 응용 프로그램이 XML을 다른 수신자 Java 응용 프로그램으로 보낼 수 있습니다. 발신자와 수신자는 공통 JAXB 라이브러리를 공유합니다. 수신기가 XML을 수신기 특정 jaxB 클래스로 만들어 제네릭 JAXB 클래스를 확장하기를 원합니다.

예시:

이것은 발신자가 사용하는 일반적인 JAXB 클래스입니다.

@XmlRootElement(name="person")
public class Person {
    public String name;
    public int age;
}

이것은 XML을 모색 할 때 사용되는 수신기 특정 JAXB 클래스입니다. 수신기 클래스에는 수신기 응용 프로그램에 맞는 논리가 있습니다.

@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
    public doReceiverSpecificStuff() ...
}

마샬링은 예상대로 작동합니다. 문제는 끊임없는 것입니다. Person 서브 클래스의 패키지 이름을 사용하여 jaxbcontext에도 불구하고 ReceiverPerson.

JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson);

내가 원하는 것은 끊임없이 말하는 것입니다 ReceiverPerson. 내가 이것을 할 수 있었던 유일한 방법은 @XmlRootElement ~에서 Person. 불행히도이 작업을 수행하면 방해가됩니다 Person 마샬에서. 마치 JAXB가 기본 클래스에서 시작하여 첫 번째를 찾을 때까지 내려 오는 것처럼 보입니다. @XmlRootElement 적절한 이름으로. 추가하려고 시도했습니다 createPerson() 반환하는 방법 ReceiverPerson 에게 ObjectFactory 그러나 그것은 도움이되지 않습니다.

도움이 되었습니까?

해결책

JAXB 2.0을 사용하고 있습니까? (JDK6 이후)

수업이 있습니다 :

javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>

하위 클래식하고 다음 방법을 재정의 할 수 있습니다.

public abstract BoundType unmarshal(ValueType v) throws Exception;
public abstract ValueType marshal(BoundType v) throws Exception;

예시:

public class YourNiceAdapter
        extends XmlAdapter<ReceiverPerson,Person>{

    @Override public Person unmarshal(ReceiverPerson v){
        return v;
    }
    @Override public ReceiverPerson marshal(Person v){
        return new ReceiverPerson(v); // you must provide such c-tor
    }
}

사용량은 다음과 같이 수행됩니다.

@Your_favorite_JAXB_Annotations_Go_Here
class SomeClass{
    @XmlJavaTypeAdapter(YourNiceAdapter.class)
    Person hello; // field to unmarshal
}

이 개념을 사용함으로써 직접 마샬링/미공개 프로세스를 제어 할 수 있다고 확신합니다 (구성 할 올바른 [Sup | Super] 유형 선택을 포함하여).

다른 팁

다음 스 니펫은 녹색 빛의 주니트 4 테스트 방법입니다.

@Test
public void testUnmarshallFromParentToChild() throws JAXBException {
  Person person = new Person();
  int age = 30;
  String name = "Foo";
  person.name = name;
  person.age= age;

  // Marshalling
  JAXBContext context = JAXBContext.newInstance(person.getClass());
  Marshaller marshaller = context.createMarshaller();

  StringWriter writer = new StringWriter();
  marshaller.marshal(person, writer);

  String outString = writer.toString();

  assertTrue(outString.contains("</person"));

  // Unmarshalling
  context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
  Unmarshaller unmarshaller = context.createUnmarshaller();
  StringReader reader = new StringReader(outString);
  RecieverPerson reciever = (RecieverPerson)unmarshaller.unmarshal(reader);

  assertEquals(name, reciever.name);
  assertEquals(age, reciever.age);
}

중요한 부분은 사용입니다 JAXBContext.newInstance(Class... classesToBeBound) 끊임없는 컨텍스트를위한 방법 :

 context = JAXBContext.newInstance(Person.class, RecieverPerson.class);

이 호출을 사용하면 JAXB는 지정된 클래스에 대한 참조 폐쇄를 계산하고 인식합니다. RecieverPerson. 테스트가 통과됩니다. 매개 변수 순서를 변경하면 java.lang.ClassCastException (그래서 그들은 ~ 해야 하다 이 순서로 전달됩니다).

서브 클래스 사람은 수신자를 위해 한 번, 발신자를 위해 한 번 두 번, XMLROOTELENTE를이 서브 클래스에만 넣습니다 (슈퍼 클래스를 떠나십시오. Person, xmlrootelement없이). 발신자와 수신기는 모두 동일한 JAXB 기본 클래스를 공유합니다.

@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
  // receiver specific code
}

@XmlRootElement(name="person")
public class SenderPerson extends Person {
  // sender specific code (if any)
}

// note: no @XmlRootElement here
public class Person {
  // data model + jaxb annotations here
}

JAXB와의 작업을 테스트하고 확인했습니다]. 상속 계층의 여러 클래스에 xmlrootelement 주석이있을 때 주목할만한 문제를 우회합니다.

이것은 또한 공통 데이터 모델을 분리하기 때문에 깔끔하고 더 많은 OO 접근법 일 것입니다. 따라서 "해결 방법"이 아닙니다.

비정상적인 동안 원하는 클래스를 인스턴스화하기 위해 사용자 정의 ObjectFactory를 만듭니다. 예시:

JAXBContext context = JAXBContext.newInstance("com.whatever.mypackage");
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.internal.bind.ObjectFactory", new ReceiverPersonObjectFactory());
return unmarshaller;

public class ReceiverPersonObjectFactory extends ObjectFactory {
    public Person createPerson() {
        return new ReceiverPerson();
    }
}

왜 당신 이이 일을하고 싶어하는지 잘 모르겠습니다 ... 그것은 나에게 안전하게 보이지는 않습니다.

수신기에서 일어날 일을 고려하면 추가 인스턴스 변수가 있습니다 ... 그러면 그 변수가 널, 0 또는 거짓 인 (또는 거짓 인 경우) ?

나는 당신이 아마 원하는 것이 그 사람 안에서 읽은 다음 그로부터 새로운 수신자를 구성하는 것이라고 생각합니다 (아마도 사람을 데려가는 생성자를 제공 할 것입니다).

핵심 요점은 "Unmarshalling에서 JAXB가 기본 클래스에서 시작하여 아래로 내려 가기"입니다. ReceiverPerson 끊임없는 경우 수업이 있습니다 Person 주석이 달라집니다 @XmlRootElement, 그래서 그것은 끊임없이 할 것입니다 Person.

따라서 제거해야합니다 @XmlRootElement 수업에서 Person, 그러나이 작업을 수행하면 방해가됩니다 Person 마샬에서.

해결책은 a를 만드는 것입니다 DummyPerson 사람을 연장하고 주석을 달았습니다 @XmlRootElement, 이것은 만든다 DummyPerson 그리고 ReceiverPerson 같은 수준에서, 그러면 마샬을 할 수 있습니다 DummyPerson 대신에 Person 그리고 unmarshal xmlstring to ReceiverPerson.

@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
    public String name;
    public int age;
}

@XmlRootElement(name = "person")
public class DummyPerson extends Person {
}

@XmlRootElement(name = "person")
public class ReceiverPerson extends Person {
     public doReceiverSpecificStuff();
}

참조 :
JAXB의 상속 지원

실제로 두 개의 별도 앱이 있으므로 수신기 앱에 다른 버전의 클래스 "Person"으로 컴파일합니다. @XmlRootElement(name="person") ~에 Person. 이것은 추악 할뿐만 아니라 발신자와 수신자 모두에 대해 동일한 사람의 정의를 사용하여 원하는 유지 가능성을 물리칩니다. 하나의 구속 기능은 그것이 작동한다는 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top