1

我有一个使用 xml 数据响应的 Web 服务器和一个使用它的客户端。两者共享相同的域代码。其中一个域对象如下所示:

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlRootElement(name = "image")
public class Image {

    private String filename;
    private ImageTypeEnum type;

    @XmlElement(name = "imageUri")
    public String getAbsoluteUri() {
        // some complex computation
        return uri;
    }
}

当我尝试将来自服务器的响应解组到该对象中时,由于没有 absoluteUri 的设置器,因此我在类中没有 imageUri。所以我像这样扩展它:

public class FEImage extends Image{
private String imageUri;
    public String getAbsoluteUri() {
        return imageUri;
    }
    public void setAbsoluteUri(String imageUri) {
        this.imageUri = imageUri;
    }   
}

我的对象工厂

@XmlRegistry
public class ObjectFactory {
    public Image createImage(){
        return new FEImage();
    }
}

我的解组代码在这里:

JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.bind.ObjectFactory",new ObjectFactory());         
((JAXBElement)unmarshaller.unmarshal((InputStream) response.getEntity())).getValue();

但是,setAbsoluteUri在解组时似乎没有被调用FEImage。当我在 中添加一个假人setAbsoluteUriImage.java,一切都按预期工作。

有人可以告诉我如何干净利落地扩展Image.java吗?

4

1 回答 1

5

注意: 我是EclipseLink JAXB (MOXy)负责人,也是JAXB 2 (JSR-222)专家组的成员。


在实例化对象时,不需要 JAXB 实现来使用ObjectFactory该类。@XmlType您可以使用注释将实例化配置为通过工厂类完成:

@XmlType(factoryClass=ObjectFactory.class, factoryMethod="createImage")
public class Image {

    private String filename;
    private ImageTypeEnum type;

    @XmlElement(name = "imageUri")
    public String getAbsoluteUri() {
        // some complex computation
        return uri;
    }
}

如果您执行上述操作,那么您的 JAXB 实现仍将使用Image该类来派生元数据,因此它不会解决您的问题。另一种方法是XmlAdapter为此用例使用一个:

更好的是,当您的域对象上的属性没有设置器时,您可以告诉您的 JAXB 实现(EclipseLink MOXy、Metro、Apache JaxMe 等)使用字段(实例变量)访问,而不是使用@XmlAccessorType(XmlAccessType.FIELD)

@XmlAccessorType(XmlAccessType.FIELD)
public class Image {
}

更新#1

如果您无法修改域对象,那么您可能会对 MOXy 的外部化元数据感兴趣。此扩展提供了一种通过 XML 为无法修改源的类提供 JAXB 元数据的方法。

了解更多信息


更新 #2 - 基于聊天结果

图片

下面是Image我将用于此示例的类的实现。对于复杂的计算,getAbsoluteUri()我只需在文件名中添加前缀“CDN”:

package forum7552310;

import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlRootElement(name = "image")
public class Image {

    private String filename;
    private ImageTypeEnum type;

    @XmlElement(name = "imageUri")
    public String getAbsoluteUri() {
        return "CDN" + filename;
    }

}

绑定.xml

下面是我整理的 MOXy 绑定文件。在这个文件中,我做了几件事:

  • 设置XmlAccessorTypeFIELD
  • 将 absoluteURI 属性标记为,XmlTransient因为我们将filename改为映射该字段。
  • 指定 XmlAdapter 将与该filename字段一起使用。这是应用在getAbsoluteUri()方法中完成的逻辑。

 

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum7552310">
    <java-types>
        <java-type name="Image" xml-accessor-type="FIELD">
            <java-attributes>
                <xml-element java-attribute="filename" name="imageUri">
                    <xml-java-type-adapter value="forum7552310.FileNameAdapter"/>
                </xml-element>
                <xml-transient java-attribute="absoluteUri"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

文件名适配器

下面是XmlAdapter应用与方法同名算法的实现getAbsoluteUri()

package forum7552310;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class FileNameAdapter extends XmlAdapter<String, String> {

    @Override
    public String marshal(String string) throws Exception {
        return "CDN" + string;
    }

    @Override
    public String unmarshal(String adaptedString) throws Exception {
        return adaptedString.substring(3);
    }

}

演示

下面是演示如何在创建 JAXBContext 时应用绑定文件的演示代码:

package forum7552310;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

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, "forum7552310/binding.xml");
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Image.class}, properties);

        File xml = new File("src/forum7552310/input.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Image image = (Image) unmarshaller.unmarshal(xml);

        System.out.println(image.getAbsoluteUri());

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

jaxb.properties

您需要在与您的类相同的包中包含一个名为jaxb.properties以下内​​容的文件Image

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

输入.xml

这是我使用的 XML 输入:

<?xml version="1.0" encoding="UTF-8"?>
<image>
    <imageUri>CDNURI</imageUri>
</image>

输出

这是运行演示代码的输出:

CDNURI
<?xml version="1.0" encoding="UTF-8"?>
<image>
   <imageUri>CDNURI</imageUri>
</image>
于 2011-09-26T12:14:53.910 回答