Recognizing types for inherited models

3 views
Skip to first unread message

tfs

unread,
Mar 31, 2011, 5:57:22 PM3/31/11
to FxWorks
Hello,

I'm currently struggeling a bit with inheritance on my models. What
i'm looking for is a way to have the serializer recognize the class
type for a property which can contain derived types. Example:

-------------------
public class BaseModel {
...
}

public class DerivedModelA extends BaseModel {
...
}
public class DerivedModelB extends BaseModel {
...
}
public class DerivedModelC extends BaseModel {
...
}

public class MainModel {
public var property:BaseModel;
}
-------------------

Now i would like to be able to set an instance of DerivedModelA,
DerivedModelB or DerivedModelC to MainModel.property, and have the
serializer correctly recognize it on serialization and
deserialization, ie when deserializing i'd like to have
MainModel.property to contain the type that was defined when
serializing, ie DerivedModelA, DerivedModelB or DerivedModelC.

I have searched through the sources, and if i'm not mistaken it all
comes down to DescriptionContext.getIncomingType, where the type is
retreived in case getRuntimeType ist set to true. As far as i
inderstood it only checks the element name and the namespace. So in
the end that would mean that i would have to create my own
serialization context so that i can override
DescriptionContext.getIncomingType to handle something like that - is
that correct?

I have a .NET background where i would simply use the XmlInclude
attribute for the base model to define the derived types, something
like this:

-------------------
[XmlInclude(typeof(DerivedModelA))]
[XmlInclude(typeof(DerivedModelB))]
[XmlInclude(typeof(DerivedModelC))]
public abstract class BaseModel {
...
}

public class MainModel {
public BaseModel property {
...
}
}
-------------------

The serializer is then able to recognize the various types for the
BaseModel typed property, and creates an appropriate type attribute
when serializing, something like this:

-------------------
<?xml version="1.0" encoding="utf-8"?>
<main xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<property xsi:type="DerivedModelA" />
</main>
-------------------

When deserializing it will use the type attribute to determine the
class type. I wish there were something similar in FlexXB :)

Regards, Tim

Alexutz

unread,
Apr 1, 2011, 2:47:25 AM4/1/11
to FxWorks
Hi Tim,

Good question, handling derived types on deserialization has always
been the Achile's heel in FlexXB. When serializing there really is no
big deal since FlexXB looks at what the object currently has so there
is no way for it to get it wrong.

I am aware of this feature in .NET and I would looove to know how they
did it :D. If you do not have namespace differentiation between
derived classes or different names then the only thing you could do
IMO is to inspect the xml contents and find elements/attributes that
allow you to identify the derived class in question.

I am sorry to say that right now there is no other way to handle this
case. I'll look more into it.

Regards,

Alex

tfs

unread,
Apr 1, 2011, 4:46:06 AM4/1/11
to FxWorks
Hi Alex,

thanks for the quick reply.

For now i've choosen the very quick and very dirty way by hacking the
core, so that the XML Schema instance type is considered on
deserialization.

-------------------
public final class XmlDescriptionContext extends DescriptionContext {

...

public override function getIncomingType(source : Object) : Class
{
var incomingXML : XML = source as XML;
if (incomingXML) {
var clasz : Class;

if
(XmlConfiguration(configuration).getResponseTypeByXsiType) {
var qn : QName = new QName('http://www.w3.org/2001/
XMLSchema-instance', 'type');
var xsiType : String =
incomingXML.attribute(qn).toString();
if (xsiType) {
clasz = getClassByAlias(xsiType);
if (clasz) {
return clasz;
}
}
}

if
(XmlConfiguration(configuration).getResponseTypeByTagName) {
var tagName : QName = incomingXML.name() as QName;
if (tagName) {
clasz = getClassByAlias(tagName.localName);
if (clasz) {
return clasz;
}
}
}

...
}
}

public final class XmlConfiguration extends Configuration {

...

public var getResponseTypeByXsiType : Boolean = true;

...

protected override function copyFrom(source : Configuration,
target : Configuration) : void{
super.copyFrom(source, target);
if(source is XmlConfiguration && target is XmlConfiguration){
XmlConfiguration(target).getResponseTypeByNamespace =
XmlConfiguration(source).getResponseTypeByNamespace;
XmlConfiguration(target).getResponseTypeByTagName =
XmlConfiguration(source).getResponseTypeByTagName;
XmlConfiguration(target).getResponseTypeByXsiType =
XmlConfiguration(source).getResponseTypeByXsiType;
XmlConfiguration(target).escapeSpecialChars =
XmlConfiguration(source).escapeSpecialChars;
}
}
}
-------------------

Regards, Tim

Alexutz

unread,
Apr 1, 2011, 8:38:58 AM4/1/11
to FxWorks
Hey, that's a very cool change you did! I think I will incorporate it
into the codebase. Did not think about the xsi:type attribute.

Good job!
> > > Regards, Tim- Ascundeţi textul citat -
>
> - Afişare text în citat -

tfs

unread,
Apr 1, 2011, 8:58:54 AM4/1/11
to FxWorks
Thanks, if you think it fits, that would be nice :)

I've gone one step further already and made it even more dirty ;)
Added automatic xsi type definition on serialization, but since i had
no time yet to dig through all the sources i really have no clue
whether this is the right place, whether it covers all possible
situations, or if it may have any nasty side effects, however until
now it seems to work fine in my situation at least.

---------------
public class XmlElementSerializer extends XmlMemberSerializer {

...

protected override function serializeObject(object : Object,
annotation : XmlMember, parentXml : XML, serializer :
SerializationCore) : void {
var child : XML = <xml />;
if (isComplexType(object) && !(object is Class)) {
if(annotation.isIDRef){
child.appendChild(serializer.getObjectId(object));
}else {
child = serializer.serialize(object,
XmlElement(annotation).serializePartialElement) as XML;

if (XmlElement(annotation).getRuntimeType) {
var xsiNamespace : Namespace = new
Namespace('xsi', 'http://www.w3.org/2001/XMLSchema-instance');
var xsiTypeQName : QName = new QName(xsiNamespace,
'type');

child.addNamespace(xsiNamespace);
child.@[xsiTypeQName] =
getQualifiedClassName(object);
}
}
}

...

}
---------------

---------------
public final class XmlDescriptionContext extends DescriptionContext
{
...

public override function getIncomingType(source : Object) : Class
{
var incomingXML : XML = source as XML;
if (incomingXML) {
var clasz : Class;

if
(XmlConfiguration(configuration).getResponseTypeByXsiType) {
var xsiNamespace : Namespace = new Namespace('xsi',
'http://www.w3.org/2001/XMLSchema-instance');
var xsiTypeQName : QName = new QName(xsiNamespace,
'type');
var xsiTypeName : String =
incomingXML.@[xsiTypeQName];
if (xsiTypeName) {
try {
clasz = getDefinitionByName(xsiTypeName) as
Class;
if (clasz) {
return clasz;
}
} catch (error : ReferenceError) {
// type doesn't exist... just do nothing?
}
}
}

if
(XmlConfiguration(configuration).getResponseTypeByTagName) {
var tagName : QName = incomingXML.name() as QName;
if (tagName) {
clasz = getClassByAlias(tagName.localName);
if (clasz) {
return clasz;
}
}
}

...
}
---------------


Regards, Tim
Reply all
Reply to author
Forward
0 new messages