Eclipse EMF:自定义XML反序列化,以便将旧项目加载到修改后的模型中

时间:2014-10-21 09:45:10

标签: java eclipse-rcp eclipse-emf emf

场合

我有一个Eclipse RCP应用程序,用于管理EMF模型中的应用程序项目。

通过将这些项目序列化为XMI格式来保存这些项目。然后可以将这些文件加载​​回模型中。我使用标准的EMF工具(例如Resource)。

由于模型重构,以下内容已更改:

  • 旧模特
    • EClass MyClass,其属性为Name(带大写字母)。
    • XMI:<MyClass Name="My Class Name 1" ... />

VS

  • 新模特
    • EClass MyClass继承自MyBaseClass,属性name(不带大写字母)。
    • EClass MyClass不再具有Name属性,因为EMF不允许这两者。这是有道理的,因为它会碰撞在例如getter方法getName()

问题

如何将 XMI项目文件加载到 new 模型中?

直到这个问题,我能够:

  • 避免修改模型
  • 扩展模型以包含旧结构和新结构,并在加载项目文件后执行修改:将信息从旧类型移动到新类型,更新引用,....

但是,在这种情况下,我无法首先加载XMI文件:模型一方面错过属性name而另一方面无法识别(因此忽略)属性Name

问题

实现此向后兼容性支持的正确位置是什么?

我假设我应该处理反序列化过程或XML映射。

解决方案的约束是:

  • 新项目(包含<MyClass name="..." ... />)也必须正确加载。
  • 保存(即序列化)项目模型应始终采用新格式!

1 个答案:

答案 0 :(得分:3)

Ed Merks在EMF forums上解决了这个问题。

背景

通过拦截XML映射来支持向后兼容性的最简单方法是在EMF资源上启用ExtendedMetaData的实现。此类是调整EMF资源及其内容的中心入口点。它避免了必须专门化EMF框架内的各种其他类。

但是,我的项目已经有一个专门的XMLHelper类,它处理XML序列化/反序列化,因此Ed Merks帮助解决了我在该类中的问题。

请注意,XMI XMLHelperImpl source code显示了在资源上启用ExtendedMetaData工具时如何调用它!

使用自定义XMLHelper

的解决方案
/**
 * Helper class that allows intercepting the XML to model mapping, to support backwards compatibility.
 * <p>
 * 2 methods must be overridden to handle compatibility mappings:
 * <dl>
 * <dt>{@link XMLHelperImpl#getFeature(EClass, String, String, boolean)}</dt>
 * <dd>Is called to map features of a certain EClass. These include attributes and child elements in the XML file.</dd>
 * <dt>{@link XMLHelperImpl#getType(EFactory, String)}</dt>
 * <dd>Is called to map types that are used in the model.</dd>
 * </dl>
 * <p>
 * Their difference becomes clear by looking at the model file. Sometimes both need to be handled. For example:
 * <ul>
 * <li>a {@link Person} has zero or more {@link Person#getPhoneNumber()} configurations ('feature')</li>
 * <li>these features are of type {@link PhoneNumber} or possibly a subclass! ('type')</li>
 * </ul>
 * <p>
 * See https://www.eclipse.org/forums/index.php/m/1449615/
 */
public class CustomXmlHelper extends XMLHelperImpl implements XMLHelper {

    public CustomXmlHelper() {
        super();
        deresolve = true;
    }

    public CustomXmlHelper(XMLResource resource) {
        super(resource);
        deresolve = true;
    }

    @Override
    public EStructuralFeature getFeature(EClass eClass, String namespaceURI, String name, boolean isElement) {
        String compatName = name;
        if (eClass == ProjectModelPackage.Literals.MyClass) {
            if (!isElement && "Name".equals(name)) {
                 // 1.x to 2.x compatibility (October 2014)
                 //   1.x = MyClass attribute 'Name'
                 //   2.x = MyBaseClass attribute 'name', shared by MyClass
                 compatName = ProjectModelPackage.Literals.EMY_BASE_CLASS__NAME.getName(); // 'n(!)ame'
            }
        }
        // future feature mappings handled here
        return super.getFeature(eClass, namespaceURI, compatName, isElement);
    }

    @Override
    public EClassifier getType(EFactory eFactory, String name) {
    String compatName = name;
        if (eFactory == ProjectModelPackage.eINSTANCE) {
            // placeholder for type compatibility
            //            if ("OldTypeName".equals(name)) {
            //                compatName = ProjectModelPackage.Literals.NEW_TYPE_NAME.getName();
            //            }
        }
        return super.getType(eFactory, compatName);
    }
}