2012-06-13 85 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; 
} 

使用通常的JAXB註釋會導致

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

但是,相反,我需要馬歇爾一樣

            (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圍繞這個問題的事情。事實上,這些呈現一些約束到我的具體情況:

  1. (A)中的Java類由JAXB生成從對應於XML結構中(B)現有的模式。我寧願不維護生成的類的修改版本。
  2. 我沒有擁有或維護上述模式。實際的模式非常大,並且經常會稍作修改。想出和維護一個等效的模式將是單調乏味的。此外,爲了跟上架構修改,我依靠JAXB自動生成類。
  3. 如果它使事情變得更容易,嵌套只能達到一個深度。在該示例中,Address不包含任何其他複雜類型。

我期待在從莫西的@XmlPath註釋,但我無法弄清楚如何獲得前綴爲(C)節點名稱。

我夢想有一個解決方案,其中一些xjc自定義提供了正確的JAXB註釋讓我繼續前進,但這看起來不太可能從我的搜索到目前爲止。任何非JAXB解決方案都已足夠。

回答

0

我解決了這個使用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

我很確定沒有這樣做的標準方法。你可以做的是創建兩個java類,一個具有層次結構,一個具有扁平結構,並使用java introspection將數據從一個複製到另一個,然後使用第二個創建xml。

+1

正如我所提到的,類是從相當大的XML模式自動生成的。所以手動維護第二組類將是令人望而卻步的。此外,當我開始編寫反射代碼時,我還想寫出我想要的XML而不是寫入另一個類。謝謝。 –

0

注:我是EclipseLink JAXB (MOXy)鉛和JAXB (JSR-222)專家小組的成員。

您可以使用的EclipseLink JAXB(莫西)外部映射文件的第二映射應用到你的對象模型,以及@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提供者,你需要在同一個包添加一個名爲jaxb.properties文件與以下條目你的域模型。

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',可以讓你獲得底層元數據的句柄並以編程方式修改它。 –

+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 @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是這種方式很簡單。另一種方式 - 解組 - 將按照預期創建對象圖。

查看Juffrou-XML here