Eclipse EMF: Персонализирайте XML десериализацията, така че старият проект да може да бъде зареден в модифициран модел

Ситуация

Имам приложение Eclipse RCP, което управлява проекти на приложения в рамките на EMF модел.

Тези проекти се записват чрез сериализирането им във формат XMI. След това такива файлове могат да бъдат заредени обратно в модела. Използвам стандартните EMF инструменти (като Resource) за това.

Поради рефакторинг на модела, следното се промени:

  • Old model
    • EClass MyClass with an attribute Name (with capital letter).
    • XMI: <MyClass Name="My Class Name 1" ... />

vs.

  • New model
    • EClass MyClass inherits from MyBaseClass, with attribute name (without capital letter).
    • EClass MyClass вече няма атрибут Name, тъй като EMF не позволява и двете. Това има смисъл, тъй като ще се сблъска напр. методът geter getName().

Проблем

Как мога да заредя стар XMI проектен файл в моя нов модел?

До този проблем успях или да:

  • избягвайте модифициране на модела
  • разрастване на модела, за да съдържа както старите, така и новите структури и извършване на модификация след зареждане на файла на проекта: преместване на информация от стари към нови типове, актуализиране на препратки,....

В този случай обаче не мога да заредя XMI файла на първо място: моделът пропуска атрибут name от една страна и не разпознава (и следователно игнорира) атрибут Name от друга.

Въпрос

Кое е правилното място за прилагане на тази поддръжка за обратна съвместимост?

Предполагам, че трябва да работя върху процеса на десериализация или XML картографирането.

Ограниченията за решението са:

  • Новите проекти (съдържащи <MyClass name="..." ... />) също трябва да се зареждат правилно.
  • Записването (т.е. сериализирането) на проектен модел трябва винаги да се случва в новия формат!

person tbacker    schedule 21.10.2014    source източник


Отговори (1)


Този въпрос беше разрешен от Ед Меркс във форумите на EMF.

Заден план

Най-чистият начин за поддържане на обратна съвместимост чрез прихващане на XML картографирането е чрез активиране на внедряване на ExtendedMetaData на вашия EMF ресурс. Този клас е централната входна точка за настройка на EMF ресурси и тяхното съдържание. Избягва необходимостта от специализиране на различни други класове в рамката на EMF.

Моят проект обаче вече имаше специализиран XMLHelper клас, който обработва XML сериализацията/десериализацията, така че Ед Меркс помогна за разрешаването на моя проблем в този клас.

Имайте предвид, че XMI XMLHelperImpl изходният код показва как се извиква инструментът 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);
    }
}
person tbacker    schedule 27.10.2014