none
如何在一个JS metro app 引用的 C#组件中序列化一个包含List<T>的类?

    问题

  • 之前我在英文论坛也提过问题,虽然得到了一些思路,但是仍然无法解决这个问题

    首先我正在开发一个JS metro app,后来引用了一个自定义的C#组件,用来进行数据定义和序列化等操作,C#组件的输出类型是winMD,我定义了一个BOOK类:

    public sealed class Book
        {
            public string ID{ get; set; }
            public string Title { get; set; }
            public string Creator { get; set; }

            public List<Chapter> Chapters { get; set; }

            public Book()
            {
                this.Chapters = new List<Chapter>();
            }

        }

    其中Chapter是另一个类,这里就不写出来了,和BOOK类类似。但是我发现当我定义public List<Chapter> Chapters { get; set; }时报出了异常,异常说的是List<T>是一个CLR类型,不是合法的WINRT类型,然后让我用IList<T>代替,于是我就照做了。

    public sealed class Book
        {
            public string ID{ get; set; }
            public string Title { get; set; }
            public string Creator { get; set; }

            public IList<Chapter> Chapters { get; set; }

            public Book()
            {
                this.Chapters = new List<Chapter>();
            }

    一切正常,之后开始写序列化类,打算把BOOK类的一个实例序列化,但是这时候问题就出现了,下面是我的序列化类

    public async void XMLSerialize(StorageFolder folder, string fileName, object instance, Type type)
            {
                XmlSerializer serializer = null;
                try
                {
                    serializer = new XmlSerializer(type);
                }
                catch (Exception ex)
                {
                }
                StorageFile newFile = await FileHelper.CreateFile(folder, fileName);
                Stream newFileStream = await newFile.OpenStreamForWriteAsync();
                serializer.Serialize(newFileStream, instance);
                newFileStream.Dispose();
            }

        }

    上面代码会catch出一个异常,就是serializer = new XmlSerializer(type);无法序列化一个接口,因为我的IList是个接口所以无法序列化。

    我去很多地方提问,英文论坛版主回复我的是将BOOK类改为internal,就可以使用LIST〈T〉了,我就试了一下,但是internal的含义是本程序集可访问,也就是说当我执行

    serializer = new XmlSerializer(type);的时候因为type是BOOK类型,而BOOK是internal的而XmlSerializer 是System.Xml.Serialization的,所以我不能用BOOK类型来

    构造我的XmlSerializer ,这样问题又回来了。

    我想序列化就必须用LIST〈T〉,但是他不是WINRT的类型所以我不能用,使用internal关键字之后又无法构造XmlSerializer 也就是无法序列化了,这样我就陷入了一个死循环。

    跪求论坛里的各位大神帮帮忙,帮我想个办法怎么解决这个问题。不会最后就要手动写序列化方法了吧。

    2012年5月8日 11:07

答案

  • 首先,肯定一点,用IList<T>接口没错,否则内部类无法构造XmlSerializer 导致无法序列化。我写过类似的序列化,我是这么处理的:

    使用 DataContractSerializer 类来做的,而不是XmlSerializer。

    public static IAsyncAction serialization()
    {
        return AsyncInfo.Run(async (token) => {
            try
            {                 
                StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, 
                    CreationCollisionOption.ReplaceExisting);
                IRandomAccessStream raStream = await file.OpenAsync(FileAccessMode.ReadWrite);
                using (IOutputStream outStream = raStream.GetOutputStreamAt(0))
                {
                    // Serialize the Session State.
                    DataContractSerializer serializer = new DataContractSerializer(typeof(type));
                    serializer.WriteObject(outStream.AsStreamForWrite(), instance);
                    await outStream.FlushAsync();
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e.Message);
            }
        });
    }

    但是注意,如此序列化后IList会最后被反序列化成数组形式,可能会导致你的JS在调用时发生问题。所以我的解决方法是在反序列化后,你的IList属性虽然接到了一个数组,但是你可以在属性的get方法中遍历这个数组将其在转化为List对象。比如我之前写的这个属性:

    [DataMember]
    private IList<string> _searchHistory = new List<string>();
    public IList<string> searchHistory
    {
        get {
            // change the deserialize search history string array data to IList
            if (_searchHistory is string[])
            {
                string[] dsHistory = _searchHistory as string[];
                _searchHistory = new List<string>();
                foreach (string s in dsHistory)
                    _searchHistory.Add(s);
            }
            // end of the change
            return _searchHistory;
        }
    }


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年5月10日 4:11
    版主

全部回复

  • 之前我在英文论坛也提过问题,虽然得到了一些思路,但是仍然无法解决这个问题

    首先我正在开发一个JS metro app,后来引用了一个自定义的C#组件,用来进行数据定义和序列化等操作,C#组件的输出类型是winMD,我定义了一个BOOK类:

    public sealed class Book
        {
            public string ID{ get; set; }
            public string Title { get; set; }
            public string Creator { get; set; }

            public List<Chapter> Chapters { get; set; }

            public Book()
            {
                this.Chapters = new List<Chapter>();
            }

        }

    其中Chapter是另一个类,这里就不写出来了,和BOOK类类似。但是我发现当我定义public List<Chapter> Chapters { get; set; }时报出了异常,异常说的是List<T>是一个CLR类型,不是合法的WINRT类型,然后让我用IList<T>代替,于是我就照做了。

    public sealed class Book
        {
            public string ID{ get; set; }
            public string Title { get; set; }
            public string Creator { get; set; }

            public IList<Chapter> Chapters { get; set; }

            public Book()
            {
                this.Chapters = new List<Chapter>();
            }

    一切正常,之后开始写序列化类,打算把BOOK类的一个实例序列化,但是这时候问题就出现了,下面是我的序列化类

    public async void XMLSerialize(StorageFolder folder, string fileName, object instance, Type type)
            {
                XmlSerializer serializer = null;
                try
                {
                    serializer = new XmlSerializer(type);
                }
                catch (Exception ex)
                {
                }
                StorageFile newFile = await FileHelper.CreateFile(folder, fileName);
                Stream newFileStream = await newFile.OpenStreamForWriteAsync();
                serializer.Serialize(newFileStream, instance);
                newFileStream.Dispose();
            }

        }

    上面代码会catch出一个异常,就是serializer = new XmlSerializer(type);无法序列化一个接口,因为我的IList是个接口所以无法序列化。

    我去很多地方提问,英文论坛版主回复我的是将BOOK类改为internal,就可以使用LIST〈T〉了,我就试了一下,但是internal的含义是本程序集可访问,也就是说当我执行

    serializer = new XmlSerializer(type);的时候因为type是BOOK类型,而BOOK是internal的而XmlSerializer 是System.Xml.Serialization的,所以我不能用BOOK类型来

    构造我的XmlSerializer ,这样问题又回来了。

    我想序列化就必须用LIST〈T〉,但是他不是WINRT的类型所以我不能用,使用internal关键字之后又无法构造XmlSerializer 也就是无法序列化了,这样我就陷入了一个死循环。

    跪求论坛里的各位大神帮帮忙,帮我想个办法怎么解决这个问题。不会最后就要手动写序列化方法了吧。

    • 已移动 chenrensongMVP 2012年5月9日 3:42 关于windows 8 metro 程序开发的问题! (发件人:Visual C#)
    • 已合并 Bob_BaoMVP, Moderator 2012年5月10日 3:45 duplicate
    2012年5月8日 11:05
  • 你好illegalusername

    由于你的问题是关于windows 8 metro 的js开发 所以我把你的文章移动到了Windows 8 Metro风格应用开发,谢谢你的理解!

    2012年5月9日 3:46
  • 首先,肯定一点,用IList<T>接口没错,否则内部类无法构造XmlSerializer 导致无法序列化。我写过类似的序列化,我是这么处理的:

    使用 DataContractSerializer 类来做的,而不是XmlSerializer。

    public static IAsyncAction serialization()
    {
        return AsyncInfo.Run(async (token) => {
            try
            {                 
                StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, 
                    CreationCollisionOption.ReplaceExisting);
                IRandomAccessStream raStream = await file.OpenAsync(FileAccessMode.ReadWrite);
                using (IOutputStream outStream = raStream.GetOutputStreamAt(0))
                {
                    // Serialize the Session State.
                    DataContractSerializer serializer = new DataContractSerializer(typeof(type));
                    serializer.WriteObject(outStream.AsStreamForWrite(), instance);
                    await outStream.FlushAsync();
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e.Message);
            }
        });
    }

    但是注意,如此序列化后IList会最后被反序列化成数组形式,可能会导致你的JS在调用时发生问题。所以我的解决方法是在反序列化后,你的IList属性虽然接到了一个数组,但是你可以在属性的get方法中遍历这个数组将其在转化为List对象。比如我之前写的这个属性:

    [DataMember]
    private IList<string> _searchHistory = new List<string>();
    public IList<string> searchHistory
    {
        get {
            // change the deserialize search history string array data to IList
            if (_searchHistory is string[])
            {
                string[] dsHistory = _searchHistory as string[];
                _searchHistory = new List<string>();
                foreach (string s in dsHistory)
                    _searchHistory.Add(s);
            }
            // end of the change
            return _searchHistory;
        }
    }


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年5月10日 4:11
    版主
  • 谢谢您的答复,万分感谢
    2012年5月10日 8:18