Flex Module中 类型转换错误

来源:互联网 发布:汉王 尚书软件 编辑:程序博客网 时间:2024/05/20 17:26

转摘的地址:http://ljhzzyx.blog.163.com/blog/static/38380312201175230919/

背景是这样,在Flex Module中使用了远程对象,并有使用实体类,获取实体类后有类型的转换;flex sdk版本是3.2。如:

[RemoteClass(alias="serviceLibrary.systemFramework.UserInfoSearchCondition")]
public class UserInfoSearchCondition

......

      第一次切换到module时是没有问题的,但第二次切换到module,在页面获取实体类进行类型转换时,报错误:

......serviceLibrary.systemFramework::UserInfoSearchCondition@XXXX无法转换为serviceLibrary.systemFramework.UserInfoSearchCondition......


      解决方法可以使用registerClassAlias(),代码如下:
import serviceLibrary.systemFramework.UserInfoSearchCondition;
registerClassAlias("serviceLibrary.systemFramework.UserInfoSearchCondition", UserInfoSearchCondition);
      这个问题在flex sdk 3.2开始出现,3.1中没有。我本机两个项目使用flex sdk 3.5,其中一个项目存在这个问题,而相同代码在另一个项目中不会有这个问题。很是奇怪。
      以上声明放在Module中即可,如果多个Module存在这样的问题,也可以放在Application里共享。
      Flex sdk 3.2 reference 文档中说明如下:
flash.net
registerClassAlias () 函数  

public function registerClassAlias(aliasName:String, classObject:Class):void

语言版本:  ActionScript 3.0
运行时版本:  AIR 1.0 Flash Player 9

当以 Action Message Format (AMF) 对一个对象进行编码时,保留该对象的类(类型)。将对象编码为 AMF 时,该函数将保存该对象的类的别名,以便在解码对象时可以恢复该类。如果编码上下文没有为对象的类注册别名,该对象将被编码为一个匿名对象。同样,如果解码上下文注册了不同的别名,将为解码后的数据创建一个匿名对象。

LocalConnection、ByteArray、SharedObject、NetConnection 及 NetStream 均为将对象编码为 AMF 的类的示例。

编码和解码上下文不必对别名使用相同的类。它们可以主动地改变类,条件是目标类包含源类序列化的所有成员。

参数  aliasName:String — 要使用的别名。
         classObject:Class — 与给定别名相关联的类。

引发 TypeError — 如果任一个参数为 null。 

      具体原因在这里有精彩描述:
http://www.myflexhero.com/share/flex-hero-flex4/flex-hero-coding-techniques/308
      如果你在使用多个Module开发,然后远程服务器返回了同一个Object,比如User对象,可能会报这样的错误:
 TypeError: Error #1034: 强制转换类型失败:无法将 User@7412zf312 转换为User 的问题。
即使你明明知道远程返回的对象就是User对象,但是不管如何,强制转换就是报错。(很霸道!)
              这里先引用一段英文:
  If moduleA brings in MyDataClass and registers it with the alias table, it has registered MyDataClass@moduleA, and when module brings in its MyDataClass and registers it, it replaces the alias with MyDataClass@moduleB and when moduleA’s result returns, the player converts it to MyDataClass@moduleB and the types don’t match.

Hopefully, the only thing in your RSL or main app or sharedcode module is the data class and maybe a few other classes that get shared.  That might eliminate the behavior differences you’re seeing.

             ok,3点解决方法。

    1. 在Module中通过使用
    registerClassAlias("com.myflexhero.domain", User);

    再次注册实体对象的Alias为当前Module,这就意味着你每次调用完远程服务器,如果返回的是User对象,你就需要调用一次改方法,建议你将所有的对象都写在一个类的静态方法里。

    2. 为你as端的实体对象使用
    [RemoteClass]
    注解,而不是
    [RemoteClass(alias="com.myflexhero.domain.User")]

    注解。这样也就意味着每次你返回User对象,你都必须想办法手动将ObjectProxy对象转换为User对象,虽然你知道它其实就是User对象。

    3. 如英文所说,将这些实体对象放置于共享模块中。

      原因就是Module中的对象,还会加上module名的后缀。

      另外摘录registerClassAlias的用法:
registerClassAlias在利用AMF3进行序列话网络通讯中,是非常有用的。需要把客户端的对象直接传送到服务器得时候,保留该对象的类(类型)。 这样的话,就可以传送自定义对象或者系统自带对象。

readObject方法对构造器有参数的类,是会出错的,会弹出参数数量不匹配这个错误。因为还原对象进行反射的时候,是默认没有参数的给构造器的。这也是有些人 讨论Sprite等对象不能进行深度拷贝的原因,所以在使用AMF3进行序列话的时候要注意这个了,还有一个就是,如果那个类包含了多个类,也就是个复合类,那么里面的那个复合类,也必须进行registerClassAlias,例如
package
{
    import flash.geom.Point;
    
    public class Test
    {
        public var name:String;
        public var point:Point;
    }
}

这样需要写两条语句才能完全把Test序列化
registerClassAlias("point",Point);
registerClassAlias("test",Test)

此示例使用 registerClassAlias() 函数为 ExampleClass 注册一个别名 ( com.example.eg )。 由于为类注册了别名,因此可以将对象作为 ExampleClass 的实例反序列化,且代码将输出 true。 如果删除 registerClassAlias() 调用,则代码将输出 false。

package {
    import flash.display.Sprite;
    import flash.net.registerClassAlias;
    import flash.utils.ByteArray;

    public class RegisterClassAliasExample extends Sprite {
        public function RegisterClassAliasExample() {
            registerClassAlias("com.example.eg", ExampleClass);
            var eg1:ExampleClass = new ExampleClass();
            var ba:ByteArray = new ByteArray();
            ba.writeObject(eg1);
            ba.position = 0;
            var eg2:* = ba.readObject();
            trace(eg2 is ExampleClass); // true
        }
    }
}

class ExampleClass {}

      另外有一种转换方式,未亲身测试,先抄录下:
http://hi.baidu.com/menglinxi_a/blog/item/cf75834c82061ef2d72afcc7.html
将通用对象转换为类实例
http://www.darronschall.com/weblog/2006/10/convert-generic-objects-into-class-instances.cfm

大意是有一个Object对象的实例myBook,它有两个属性,name,price
我们有一个类Book,如何将myBook转换为Book呢?
注意myBook是Object类型的,无法Book(myBook)这样进行强制转换
伟大的作者在看到无数条类似问题后,给出了他的解决方案
var book:Book = ObjectTranslator.objectToInstance( myBook, Book ) as Book;
多么简洁,多么强大!!

This questionhas comeupa lotonFlexCodersrecently, so I wanted to offer to the community a generic solution to convert plain old vanilla objects into full-fledged class instances.

I had originally developed this solution to be used with theJSON library that I made for the Adobe Labs Corelib project, but it's really an all-purpose solution. Essentially, my utility method allows you to turn any remote service that returns plain old ActionScript objects (like JSON, WebServices, etc) into a service that returns typed class instances, much like RemoteObject.

This makes it easier to employ the ValueObject pattern... instead of getting back an object with some properties attached, I can convert that object into, say, a Book ValueObject. The Book then is easier to work with because I get IDE and compiler support from strong typing, and it also speeds up the code execution since accessing values in typed objects executes faster than in non-typed objects.

You can download my ObjectTranslator, and its test cases, here. (MIT License)

Usage is as follows:

import com.darronschall.examples.vo.Book;
import com.darronschall.serialization.ObjectTranslator;
// Define an object with properties that mimic the variable names
// inside of the Book class
var bookObj:Object = { title: "My Book title", pageCount: 10, inLibrary: true };
// Convert the generic object into an instance of the Book class
var book:Book = ObjectTranslator.objectToInstance( bookObj, Book ) as Book;

Yup, it's really that simple.

Unfortunately, things get a little more complicated when you have nested ValueObjects. I didn't take the time to make my method recursive, so it's up to you to first convert the nested objects into instances, and then convert the top level object into an instance. It's a little more work that can avoided by some recursion. I've outlined what the algorithm would look like inside of the code, I just haven't had the time to write it yet.

import com.darronschall.examples.vo.Book;
import com.darronschall.examples.vo.Student;
import com.darronschall.serialization.ObjectTranslator;
var studentObj:Object = { firstName: "test first", lastName: "test last", favoriteBook: { title: "Favorite Book!" } };
// First we need to convert the nested objects to classes studentObj.
favoriteBook = ObjectTranslator.objectToInstance( studentObj.favoriteBook, Book );
// Convert the student object to a Student class
var student:Student = ObjectTranslator.objectToInstance( studentObj, Student ) as Student;

So, to summarize, the objectToInstance method allows JSON and WebService services to return true class instances from the server, making them behave like RemoteObject and allowing developers to more easily employ the ValueObject pattern. Use the objectToInstance method on the object that the service returns, and then use the converted instance elsewhere in your code.

Those who are curious about the implementation will get a kick out of the simplicity of my algorithm. Essentially, all I'm doing is constructing an AMF packet with the class name and the object property values, then doing a byteArray.readObject() to convert the AMF packet into a class instance. The code is fairly well commented so you can follow along easily. I'd like to think this is one of those "so simple it's brilliant" type of moments...

Have fun, and let me know if you run into any issues!


如果鸟语生疏的话,直接看代码吧
You can download my ObjectTranslator, and its test cases, here. (MIT License)

那么这个有什么用呢?
比如,我现在在开发一个基于AIR+sqlite的东东,从sqlite返回的每行数据被封装在一个Object类型的实例中,但是针对每张表我都建立了相关的VO,将这个Object转换为VO就是比较有意思的事情了,幸好有作者的无私奉献,终于能少死一些脑细胞了。
同样的,在做Flex+WebService的时候,返回的数据可能是Object或者ObjectProxy,那么用这个方法可以很轻松的将其转换为我们所需要的类型。
Enjoy!!

      关于类型转换:
假设有一个类叫做Class1,我们声明了一个它的对象 c1,如果想要将它转换成Class2类型,只要这样写:

Class2(c1);

在AS3中你依然可以这样写,但是AS3 中提供了一个新的操作符: as ,并且推荐使用as 进行强制转换,上述的例子用 as 操作符实现就是这样:

c1 as Class2;

使用 as 操作符有几个好处:

1.它的效果和第一种方法是一样的。
2.如果类型不兼容无法转换,就会返回null,而不是出错。这样你就可以自定义错误的时候该做什么。
3.没有运行时错误(Run Time Error)提示。

不过有些时候我在使用 as 的时候并不能达到强制转换的目的,而使用第一种方法则可以。为什么 as 操作符有时候会不好用呢?在AS3.0类库中最高层类(Top Level classes)之间进行强制转换时, as 操作符是不起作用的。比如,假如你想要将一个String 类型的字符串 str 转换成 Number 类型的数字 num 时,可能想要这样写:

num = str as Number;

这样写是没有用的,你只能通过第一种方法来达到强制转换的目的:

num = Number(str);

      ok,最后感慨下:万恶的社会啊!
原创粉丝点击