2012-06-13 5 views
3

구성 요소 객체가 중첩 된 XML 요소가되는 대신 해당 유형의 속성이 루트 요소의 직계 자식이되고 이름 앞에 유형이있는 객체 구조를 마샬링 할 수 있습니까? . 주어진 예를 들어중첩 된 객체를 "평면"XML 구조로 마샬링

:

            (A) 
public class Customer { 

    protected String firstName; 
    protected String lastName; 
    protected Address address; 
} 

public class Address { 

    protected String street; 
    protected String city; 
} 

을 대신

            (B) 
<customer> 
    <firstName>Juan</firstName> 
    <lastName>dela Cruz</lastName> 
    <address> 
     <street>123 Rizal Avenue</street> 
     <city>Manila</city> 
    </address> 
</customer> 

발생하지만 것이 일반적인 JAXB 주석을 사용하여, 나는

            (C) 
<customer> 
    <firstName>Juan</firstName> 
    <lastName>dela Cruz</lastName> 
    <address_street>123 Rizal Avenue</address_street> 
    <address_city>Manila</address_city> 
</customer> 

그것을 마샬 동일한 필요 이미 JAXB를 사용하고 있기 때문에 JAXB의 요구 사항을 해결할 JAXB 주문이 있다면 이 문제를 둘러싼 것들. 사실,이 내 특정 상황에 몇 가지 제약 조건을 제시 : (A)에서

  1. 자바 클래스는 JAXB에 의해 생성을 (B)의 XML 구조에 해당하는 기존 스키마에서. 생성 된 클래스의 수정 된 버전을 유지하지 않는 것이 좋습니다.
  2. 본인은 상기 스키마를 소유하거나 유지하지 않습니다. 실제 스키마는 상당히 크며 사소한 수정이 필요할 수 있습니다. 그와 동등한 스키마를 고안하고 유지하는 것은 지루할 것입니다. 또한 스키마 수정을 따라 가기 위해 JAXB에 의한 자동화 된 클래스 생성에 의존합니다.
  3. 작업을 쉽게 수행 할 수있는 경우 중첩은 최대 한 수준까지만 가능합니다. 예에서 Address은 다른 복합 유형을 포함하지 않습니다.

나는 목시에서 @XmlPath 주석 찾고 있어요,하지만 난 (C) 같이 접두사 노드 이름을 얻는 방법을 알아낼 수 없습니다.

나는 올바른 JAXB 주석을 제공하는 일부 xjc 사용자 정의가 나를 끌어들일 수있는 솔루션을 꿈꾸지 만 지금까지는 내 검색에서 그렇게 보이지 않습니다. JAXB가 아닌 다른 솔루션으로도 충분합니다.

답변

0

는 I는 XStream Converter를 사용해 해결. @XmlType 어노테이션이 있는지 확인하여 JAXB bean이 변환 중인지 판별합니다. 다른 모든 유형은 기본 변환기를 거칩니다.

비록 JAXB 중심의 솔루션이 좋았을 지 모르지만 XStream은 강력하고 직접적인 솔루션을 제공했습니다.

public class FlatXmlConverter implements Converter { 

    private static final Logger log = 
      LoggerFactory.getLogger(NvpConverter.class); 

    @Override 
    public void marshal(Object source, HierarchicalStreamWriter writer, 
      MarshallingContext context) { 
     Class<? extends Object> sourceClass = source.getClass(); 
     String prefix = (String) context.get("prefix"); 
     for (Field field : sourceClass.getDeclaredFields()) { 
      if (!field.isAccessible()) { 
       field.setAccessible(true); 
      } 
      String name = field.getName(); 
      Class<?> type = field.getType(); 

      try { 
       Object value = field.get(source); 
       if (value != null) { 
        if (type.isAnnotationPresent(XmlType.class)) { 
         context.put("prefix", name); 
         context.convertAnother(value); 
         context.put("prefix", null); 
        } else { 
         String nodeName; 
         if (prefix == null) { 
          nodeName = name; 
         } else { 
          nodeName = prefix + "_" + name; 
         } 

         writer.startNode(nodeName); 
         context.convertAnother(value); 
         writer.endNode(); 
        } 
       } 
      } catch (IllegalArgumentException ex) { 
       log.error("IllegalArgumentException", ex); 
      } catch (IllegalAccessException ex) { 
       log.error("IllegalAccessException", ex); 
      } 
     } 
    } 

    @Override 
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    @Override 
    @SuppressWarnings({"rawtypes", "unchecked"}) 
    public boolean canConvert(Class type) { 
     log.debug("canConvert({})", type); 
     return type.isAnnotationPresent(XmlType.class); 
    } 
} 
0

나는 이것을하기위한 표준 방법이 없다고 확신한다. 당신이 할 수있는 것은 두개의 자바 클래스를 생성하는 것입니다. 하나는 계층 구조로, 다른 하나는 평면 ​​구조로 만들고, 자바 인트로 스펙 션을 사용하여 데이터를 다른 것으로 복사 한 다음 두 번째를 사용하여 xml을 만듭니다.

+1

앞서 언급했듯이 클래스는 상당히 큰 XML 스키마에서 자동으로 생성됩니다. 따라서 수동으로 두 번째 클래스 집합을 유지하는 것은 금지됩니다. 또한, 반사 코드를 작성할 때쯤에는 다른 클래스에 쓰는 대신 원하는 XML을 작성하는 것만으로도 충분합니다. 감사. –

0

주 :은 내가 EclipseLink JAXB (MOXy) 리드와 JAXB (JSR-222) 전문가 그룹의 구성원입니다.

EclipseLink JAXB (MOXy)에서 외부 맵핑 파일을 사용하여 오브젝트 모델에 두 번째 맵핑을 적용하고 @XmlPath 확장자를 사용하여 오브젝트 모델을 병합 할 수 있습니다.

외부 매핑 파일 (oxm.xml)

다음은 외부 매핑 파일이 어떻게 보이는지있는 패키지에 forum11007814라는 모델 클래스의 경우.다음은

<?xml version="1.0"?> 
<xml-bindings 
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" 
    package-name="forum11007814" 
    xml-accessor-type="FIELD"> 
    <java-types> 
     <java-type name="Customer"> 
      <xml-root-element/> 
      <java-attributes> 
       <xml-element java-attribute="address" xml-path="."/> 
      </java-attributes> 
     </java-type> 
     <java-type name="Address"> 
      <java-attributes> 
       <xml-element java-attribute="street" name="address_street"/> 
       <xml-element java-attribute="city" name="address_city"/> 
      </java-attributes> 
     </java-type> 
    </java-types> 
</xml-bindings> 

데모

JAXBContext를 만들 때 목시의 외부 매핑 파일을 활용하는 방법을 보여줍니다 코드입니다.

package forum11007814; 

import java.io.File; 
import java.util.*; 
import javax.xml.bind.*; 
import org.eclipse.persistence.jaxb.JAXBContextFactory; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     Map<String, Object> properties = new HashMap<String,Object>(1); 
     properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11007814/oxm.xml"); 
     JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties); 

     File xml = new File("src/forum11007814/c.xml"); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     Customer customer = (Customer) unmarshaller.unmarshal(xml); 

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

} 

jaxb.properties

는 다음과 같은 항목을 사용하여 도메인 모델과 동일한 패키지에 jaxb.properties을라는 파일을 추가 할 필요가 당신의 JAXB 공급자로 MOXY을 지정합니다.

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

c.xml/출력

<?xml version="1.0" encoding="UTF-8"?> 
<customer> 
    <firstName>Juan</firstName> 
    <lastName>dela Cruz</lastName> 
    <address_street>123 Rizal Avenue</address_street> 
    <address_city>Manila</address_city> 
</customer> 
+0

매핑을 위해 다른 XML 파일을 쓰는 것에 조심 스럽습니다. 원래 클래스가 생성되는 XSD는 이미 상당히 크기 때문에 그러한 매핑이 동일하게 보일 것으로 보입니다. 필요한 매핑이 기계적이라고 생각하면 MOXy에서 이러한 매핑을 프로그래밍 방식으로 제공하는 방법이 있습니까? –

+0

@Edward - 매핑 파일은 메타 데이터를 재정의하는 데만 필요하므로 원본 XSD보다 훨씬 작을 것으로 예상됩니다. '@XmlCustomizer'와'SessionEventListener'와 같은 훅 (hook)이있어서 기본 메타 데이터에 대한 핸들을 얻어 프로그래밍 방식으로 수정할 수 있습니다. –

+0

[@XmlCustomizer] (http://www.eclipse.org/eclipselink/api/2.0/org/eclipse/persistence/oxm/annotations/XmlCustomizer.html) 및 [SessionEventListener] (http : // www. .eclipse.org/eclipselink/api/2.0/org/eclipse/persistence/sessions/SessionEventListener.html) (나는 올바른 장소를보고 있습니까?) 전에 MOXy를 사용한 적이 없습니다. 내 머리를 감싸는 데 시간이 좀 걸릴 수 있습니다. –

-1

@XmlID a @XmlIDREF를 사용하십시오. 그리고 모든 객체 목록으로 클래스를 만듭니다.

0

참고 : 나는 당신이 간단한 방법으로 그렇게 할 수 juffrou-XML의 창조자이다 : 단지 XML 매핑 파일에이 구성 :

<root-element xml="Customer" type="org.yourpackage.Customer"> 
    <element property="firstName" /> 
    <element property="lastName" /> 
    <element property="address.street" xml="address_street"/> 
    <element property="address.city" xml="address_city"/> 
</root-element> 

<root-element xml="Address" type="org.yourpackage.Address" /> 

마샬링 중첩 된 콩을 편평한 XML의 struture로는 이런 식으로 쉽게. 그리고 다른 방법으로는 비 정렬 화 (unmarshalling)가 예상대로 객체 그래프를 생성합니다.

Juffrou-XML here을 확인하십시오.

관련 문제