emfjson / emfjson-jackson

JSON Binding for Eclipse Modeling Framework
https://emfjson.github.io
Other
80 stars 23 forks source link

attribute serialization #58

Closed lex-em closed 8 years ago

lex-em commented 9 years ago

Hi,

I have a problen with json serialization of ecore model. I'm trying to create .uml and .json (with emfjson) files like this:

ResourceSet umlResourceSet;

    @Before
    public void prepare(){
        umlResourceSet = new ResourceSetImpl();
        umlResourceSet.setURIConverter(URIConverter.INSTANCE);
        umlResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("json", new JsonResourceFactoryCit());
        umlResourceSet.getPackageRegistry().put(EcorePackage.eNS_URI, EcorePackage.eINSTANCE);
        UMLResourcesUtil.init(umlResourceSet);
    }

    @Test
    public void test(){
        List<EObject> eObjects = new ArrayList<>();
        List<String> useCaseNames = Arrays.asList("Кредитовый перевод",
                "Инициирование кредитового перевода", "Коммерческий платеж",
                "Налоговый платеж", "Валютный платеж", "Межбанковский перевод", "Уведомление кредитора");
        List<String> actorNames = Arrays.asList("Дебитор", "Банк дебитора", "Банк-посредник",
                "Кредитор", "Банк кредитора");
        Map<String, UseCase> useCaseList = new HashMap<>();
        useCaseNames.stream().forEach(n -> {
            UseCase uc = UMLFactory.eINSTANCE.createUseCase();
            uc.setName(n);
            useCaseList.put(n, uc);
        });
        org.eclipse.uml2.uml.Package p = UMLFactoryImpl.init().createPackage();
        eObjects.add(p);
        Resource primitiveLib = umlResourceSet.getResource(URI.createURI(UMLResource.UML_PRIMITIVE_TYPES_LIBRARY_URI), true);
        Package primitiveLibPackage = (Package) EcoreUtil.getObjectByType(primitiveLib.getContents(), UMLPackage.Literals.PACKAGE);
        p.createPackageImport(primitiveLibPackage);
        p.setName("NPModel");
        Resource profileLib = umlResourceSet.getResource(URI.createURI("model/ISO20022Profile.profile.uml"), true);
        Package isoP = (Package) EcoreUtil.getObjectByType(profileLib.getContents(), UMLPackage.Literals.PACKAGE);
        p.applyProfile(isoP.containingProfile());
        actorNames.stream().forEach(n -> p.createPackagedElement(n, UMLFactoryImpl.getPackage().getActor()));
        isoP.containingProfile().getOwnedStereotypes().stream()
                .filter(s -> s.getName().contains("BusinessRole")).findFirst().ifPresent(st -> {
            NamedElement el = p.getPackagedElement("Банк дебитора");
            eObjects.add(el.applyStereotype(st));
        });
        useCaseList.forEach((n, uc) -> p.getPackagedElements().add(uc));
        Resource resource = umlResourceSet.createResource(URI.createURI("test.uml"));
        resource.getContents().addAll(eObjects);
        try {
            resource.save(null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Resource resourceJSON = umlResourceSet.createResource(URI.createURI("test.json"));
        resourceJSON.getContents().addAll(eObjects);
        try {
            resourceJSON.save(null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Created .uml is right:

<?xml version="1.0" encoding="UTF-8"?>
<xmi:XMI xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ISO20022Profile="http:///schemas/ISO20022Profile/_Z1baoCR_EeW-eZ1ZLTAX7g/50" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xsi:schemaLocation="http:///schemas/ISO20022Profile/_Z1baoCR_EeW-eZ1ZLTAX7g/50 model/ISO20022Profile.profile.uml#_Z1fsECR_EeW-eZ1ZLTAX7g">
  <uml:Package xmi:id="_eJYk0CquEeWRmPUlcrfDwQ" name="NPModel">
    <packageImport xmi:id="_eJYk0SquEeWRmPUlcrfDwQ">
      <importedPackage xmi:type="uml:Model" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#_0"/>
    </packageImport>
    <packagedElement xmi:type="uml:Actor" xmi:id="_eJYk0iquEeWRmPUlcrfDwQ" name="Дебитор"/>
    <packagedElement xmi:type="uml:Actor" xmi:id="_eJYk0yquEeWRmPUlcrfDwQ" name="Банк дебитора"/>
    <packagedElement xmi:type="uml:Actor" xmi:id="_eJYk1CquEeWRmPUlcrfDwQ" name="Банк-посредник"/>
    <packagedElement xmi:type="uml:Actor" xmi:id="_eJYk1SquEeWRmPUlcrfDwQ" name="Кредитор"/>
    <packagedElement xmi:type="uml:Actor" xmi:id="_eJYk1iquEeWRmPUlcrfDwQ" name="Банк кредитора"/>
    <packagedElement xmi:type="uml:UseCase" xmi:id="_eJYk1yquEeWRmPUlcrfDwQ" name="Инициирование кредитового перевода"/>
    <packagedElement xmi:type="uml:UseCase" xmi:id="_eJYk2CquEeWRmPUlcrfDwQ" name="Межбанковский перевод"/>
    <packagedElement xmi:type="uml:UseCase" xmi:id="_eJYk2SquEeWRmPUlcrfDwQ" name="Коммерческий платеж"/>
    <packagedElement xmi:type="uml:UseCase" xmi:id="_eJZL4CquEeWRmPUlcrfDwQ" name="Уведомление кредитора"/>
    <packagedElement xmi:type="uml:UseCase" xmi:id="_eJZL4SquEeWRmPUlcrfDwQ" name="Кредитовый перевод"/>
    <packagedElement xmi:type="uml:UseCase" xmi:id="_eJZL4iquEeWRmPUlcrfDwQ" name="Налоговый платеж"/>
    <packagedElement xmi:type="uml:UseCase" xmi:id="_eJZL4yquEeWRmPUlcrfDwQ" name="Валютный платеж"/>
    <profileApplication xmi:id="_eJZL5CquEeWRmPUlcrfDwQ">
      <eAnnotations xmi:id="_eJZL5SquEeWRmPUlcrfDwQ" source="http://www.eclipse.org/uml2/2.0.0/UML">
        <references xmi:type="ecore:EPackage" href="model/ISO20022Profile.profile.uml#_Z1fsECR_EeW-eZ1ZLTAX7g"/>
      </eAnnotations>
      <appliedProfile href="model/ISO20022Profile.profile.uml#_rspygA6UEeWHY4KRRC3kUQ"/>
    </profileApplication>
  </uml:Package>
  <ISO20022Profile:BusinessRole xmi:id="_eJZL5iquEeWRmPUlcrfDwQ" base_NamedElement="_eJYk0yquEeWRmPUlcrfDwQ" base_Actor="_eJYk0yquEeWRmPUlcrfDwQ"/>
</xmi:XMI>

But .json is not, there no attributes (in this case "name" attribute):

[
  {
    "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//Package",
    "_id": "_ezM94CquEeWRmPUlcrfDwQ",
    "packageImport": [
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//PackageImport",
        "_id": "_ezM94SquEeWRmPUlcrfDwQ",
        "visibility": "public",
        "importedPackage": {
          "$ref": "pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#_0"
        },
        "importingNamespace": {
          "$ref": "_ezM94CquEeWRmPUlcrfDwQ"
        }
      }
    ],
    "packagedElement": [
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//Actor",
        "_id": "_ezM94iquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//Actor",
        "_id": "_ezM94yquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//Actor",
        "_id": "_ezM95CquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//Actor",
        "_id": "_ezM95SquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//Actor",
        "_id": "_ezM95iquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//UseCase",
        "_id": "_ezM95yquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//UseCase",
        "_id": "_ezM96CquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//UseCase",
        "_id": "_ezM96SquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//UseCase",
        "_id": "_ezM96iquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//UseCase",
        "_id": "_ezM96yquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//UseCase",
        "_id": "_ezM97CquEeWRmPUlcrfDwQ"
      },
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//UseCase",
        "_id": "_ezM97SquEeWRmPUlcrfDwQ"
      }
    ],
    "profileApplication": [
      {
        "eClass": "http://www.eclipse.org/uml2/5.0.0/UML#//ProfileApplication",
        "_id": "_ezM97iquEeWRmPUlcrfDwQ",
        "eAnnotations": [
          {
            "eClass": "http://www.eclipse.org/emf/2002/Ecore#//EAnnotation",
            "_id": "_ezM97yquEeWRmPUlcrfDwQ",
            "source": "http://www.eclipse.org/uml2/2.0.0/UML",
            "references": [
              {
                "$ref": "model/ISO20022Profile.profile.uml#_Z1fsECR_EeW-eZ1ZLTAX7g"
              }
            ]
          }
        ],
        "appliedProfile": {
          "$ref": "model/ISO20022Profile.profile.uml#_rspygA6UEeWHY4KRRC3kUQ"
        },
        "applyingPackage": {
          "$ref": "_ezM94CquEeWRmPUlcrfDwQ"
        }
      }
    ]
  },
  {
    "eClass": "model/ISO20022Profile.profile.uml#_Z1hhpyR_EeW-eZ1ZLTAX7g",
    "_id": "_ezNk8CquEeWRmPUlcrfDwQ",
    "registrationStatus": "PROVISIONALLY_REGISTERED ",
    "base_NamedElement": {
      "$ref": "_ezM94yquEeWRmPUlcrfDwQ"
    },
    "base_Actor": {
      "$ref": "_ezM94yquEeWRmPUlcrfDwQ"
    }
  }
]

What happen? What I should do?

lex-em commented 9 years ago

Library version is 0.10.0 In 0.11.0 "_id" attribute do not serialized too.

lex-em commented 9 years ago

Attributes filtered in org.emfjson.jackson.streaming.StreamWriter.generate() on line 151, here calls org.emfjson.common.EObjects.isCandidate():

    public static boolean isCandidate(EObject object, EAttribute attribute) {
        return (object.eIsSet(attribute) || attribute.getEType() instanceof EEnum) && 
                !attribute.isDerived() && 
                !attribute.isTransient() && 
                !attribute.isUnsettable();
    }

For some needed attributes object.eIsSet(attribute) is false or !attribute.isUnsettable() is false.

Why this filter needs?

ghillairet commented 9 years ago

Checking that isUnsettable in that method is a mistake, will be remove in next version, see #59 To get ids in 0.11, explicitly set the option like this:

Map<String, Object> options = new HashMap<>();
options.put(EMFJs.OPTION_USE_ID, true);

resource.save(options);
lex-em commented 9 years ago

Yes, but it realy don't work until Resource implementation, that also implenets UuidResource interface, do not return true in UuidResource.useUUIDs().

I think it realy needed to use custom JsonResourceFactory with custom JsonResource that overrided useUUIDs(). For this aim need to set custom JsonResourceFactory to EObjectDeserializer and ResourceDeserializer.

It works for me, if needed I can do a pull request.

ghillairet commented 9 years ago

That's the correct behavior, the default id serializer will serialize the fragment of the object's URI only if the option USE_ID is true. For the fragment to be a UUID you need to have a Resource implementation that supports UUID generation (like XMIResource, JsonResource) and overwrite the method useUUIDs() to return true.

Since 0.11 you can also use custom serializers for ids, see cdo sample.

lex-em commented 9 years ago

Yes, but what you think about custom JsonResourceFactory in EObjectDeserializer and ResourceDeserializer?

ghillairet commented 9 years ago

What are you thinking about? Not sure I understand the need to use a ResourceFactory in those deserializers.

lex-em commented 9 years ago

How I can configure deserializer with custom JsonResource?

lex-em commented 9 years ago

For this purpose I realize ResourceFactory and expect my custom JsonResource, but I get a standart JsonResource

ghillairet commented 9 years ago

What are you trying to achieve? If you want to use a custom resource implementation you need to specify it through the resourceSet.

lex-em commented 9 years ago

Yes, but your deserializer creates only yours JsonResource

ghillairet commented 9 years ago

Are you referring to this https://github.com/ghillairet/emfjson/blob/master/emfjson-jackson/src/main/java/org/emfjson/jackson/databind/deser/ResourceDeserializer.java#L86

If so, you should use the object mapper context to tell which resource is the target, like here https://github.com/ghillairet/emfjson/blob/master/emfjson-jackson/src/main/java/org/emfjson/jackson/resource/JsonResource.java#L56