C#学习教程:.NET无法反序列化嵌套结构?分享


.NET无法反序列化嵌套结构?

我遇到了使C#(VS2008,Compact Framework,.NET是版本3.5 SP1)成功反序列化嵌套结构的问题。 当我在移动设备的模拟器上运行时(我正在使用“Pocket PC 2003 Second Edition”模拟器),问题只出现在CF中,在我的Windows机器上运行的完全相同的代码没有同样的问题。

这是我的代码:

public struct Fred { public string Name; } public struct Middle { public Fred[] Freds; } public struct Top { public Middle Middle; public Fred[] Freds; } public static void Test() { Top top = new Top(); top.Middle.Freds = new Fred[2]; top.Middle.Freds[0].Name = "Fred20"; top.Middle.Freds[1].Name = "Fred21"; top.Freds = new Fred[2]; top.Freds[0].Name = "Fred10"; top.Freds[1].Name = "Fred11"; StringBuilder sb = new StringBuilder(); System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(top.GetType()); using (StringWriter sw = new StringWriter(sb)) { x.Serialize(sw, top); } string xml = sb.ToString(); string[] lines = xml.Split(new char[] { 'r', 'n' }); foreach (string line in lines) { Debug.WriteLine(" " + line.Trim()); } MemoryStream ms = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(xml)); StreamReader sr = new StreamReader(ms); object o = x.Deserialize(sr); Debug.WriteLine("Deserialized into " + o); Top go2 = (Top)o; if (go2.Freds == null) Debug.WriteLine(" go2.Freds is null"); else Debug.WriteLine(" go2.Freds[0].Name is "" + go2.Freds[0].Name + """); if (go2.Middle.Freds == null) Debug.WriteLine(" go2.Middle.Freds is null"); else Debug.WriteLine(" go2.Middle.Freds[0].Name is "" + go2.Middle.Freds[0].Name + """); } 

当我运行它时,它创建的XML看起来很好:

      Fred20   Fred21      Fred10   Fred11    

但是C#无法成功反序列化这个XML – 控制台输出是这样的:

 Deserialized into Top go2.Freds[0].Name is "Fred10" go2.Middle.Freds is null 

xsd有类似的问题:

                             

我刚遇到C#错误吗? 还是我错过了一些明显的东西?

注意:使用该名称两次不是问题,如果我创建一个名为George的结构与Fred相同,并将Middle的内容更改为公共George [] George,问题就不会更好了。

TLDR(用于撇油器):这篇文章由两部分组成。

第1部分:Protobuf的快速介绍。 这里使用属性。

第2部分:问题的实际答案:配置序列化而不修改inheritance的库

好的,我会试一试。

问题似乎是您正在使用Compact Framework,它不具备与完整.NET框架相同的序列化/反序列化function。 所以我们需要一些自定义序列化。

遵循Compact Framework的理念,我的猜测是你也想要一些表现良好且占地面积小的东西。 所以我为这个任务挑选了Protobuf (也比XmlSerializer快了大约12倍 )

您可以通过运行以下命令来安装它:

 Install-Package protobuf-net 

让我们从简单的方法开始 – 通过向模型添加属性。 接下来是没有属性的配置,因为您指出原始模型不能/不应该被修改。 这仅用于说明。

使用适当的属性装饰,您的模型将如下所示:

第1部分:使用属性配置。

我再说一遍,这部分仅用于说明目的 – 请继续阅读“没有属性的配置”

 [ProtoContract] public struct Fred { [ProtoMember(1)] public string Name; } [ProtoContract] public struct Middle { [ProtoMember(1)] public Fred[] Freds; } [ProtoContract] public struct Top { [ProtoMember(1)] public Middle Middle; [ProtoMember(2)] public Fred[] Freds; } 

这里唯一需要注意的是编号成员的使用,称为密钥。 它与在JSON或XML序列化的情况下为它们提供属性名称基本相同,除了这是实现它的protobuf方法。 您只需为同一个类中的每个成员分配一个唯一的整数值,并且大部分时间都是在那里完成的。

为方便起见,我们添加一个简单的Builder,我们可以从中实例化一个类似于示例中的Top

 public class TopTestBuilder { public Top BuildDefaultTestTop() { var top = new Top { Middle = new Middle { Freds = new[] { new Fred {Name = "Fred20"}, new Fred {Name = "Fred21"} } }, Freds = new[] { new Fred {Name = "Fred10"}, new Fred {Name = "Fred11"} } }; return top; } } 

我们可以像这样序列化它:

 Top topIn = new TopTestBuilder().BuildDefaultTestTop(); string serialized; using (var stream = new MemoryStream()) { Protobuf.Serializer.Serialize(stream, topIn); stream.Position = 0; var reader = new StreamReader(stream); serialized = reader.ReadToEnd(); } // Output: "nDC4nbnACKFred20nbnACKFred21DC2bnACKFred10DC2bnACKFred11" 

并将其反序列化为

 Top topOut; using (var stream = new MemoryStream()) { var writer = new StreamWriter(stream); writer.Write(serialized); writer.Flush(); stream.Position = 0; topOut = Protobuf.Serializer.Deserialize(stream); } 

正如您所看到的, MemoryStreams有一些管道,但除此之外,它应该看起来熟悉其他类型的序列化工作。 同样,一切都可以通过配置自定义TypeModel来完成,从而允许序列化与模型完全分离。

第2部分:没有属性的配置

默认情况下,Protobuf使用属性来定义TypeModel ,然后将其存储在ProtoBuf.Meta.RuntimeTypeModel.Default 。 直接调用静态Protobuf.Serializer时使用此属性。 我们也可以定义自己的。 它需要一些摆弄(注意自己:RTFM)才能使它工作但事实certificate它几乎一样简单:

 var model = TypeModel.Create(); // The first parameter (maps to ProtoContractAttribute) is the Type to be included. // The second parameter determines whether to apply default behavior, // based on the attributes. Since we're not using those, this has no effect. model.Add(typeof(Fred), false); model.Add(typeof(Middle), false); model.Add(typeof(Top), false); // The newly added MetaTypes can be accessed through their respective Type indices. // The first parameter is the unique member number, similar to ProtoMemberAttribute. // The second parameter is the name of the member as it is declared in the class. // When the member is a list: // The third parameter is the Type for the items. // The fourth parameter is the Type for the list itself. model[typeof(Fred)].Add(1, "Name"); model[typeof(Middle)].Add(1, "Freds", typeof(Fred), typeof(Fred[])); model[typeof(Top)].Add(1, "Middle"); model[typeof(Top)].Add(2, "Freds", typeof(Fred), typeof(Fred[])); 

现在我们所要做的就是为这两个函数更改一行代码:

连载:

 Top topIn = new TopTestBuilder().BuildDefaultTestTop(); string serialized; using (var stream = new MemoryStream()) { model.Serialize(stream, top); stream.Position = 0; var reader = new StreamReader(stream); serialized = reader.ReadToEnd(); } 

反序列化:

 Top topOut; using (var stream = new MemoryStream()) { var writer = new StreamWriter(stream); writer.Write(serialized); writer.Flush(); stream.Position = 0; topOut = (Top) _model.Deserialize(stream, null, typeof (Top)); } 

它的工作方式也一样。 也许添加一个类来保持组织有序 – 给它两个公共方法SerializeDeserialize ,以及一个私有方法BuildTypeModel (从构造函数调用并存储在序列化程序的字段中?)

您的调用代码最终会看起来像这样:

 var serializer = new CustomProtoBufSerializer(); var serialized = serializer.Serialize(someClassInput); SomeClass someClassOutput = serializer.Deserialize(serialized); 

有一件事很快变得清晰 – Protobuf没有像大多数JSON和XML序列化器那样经过详尽的记录和测试。 在某些情况下,这与序列化结果一起对人类不可读是一个缺点。 除此之外,它似乎快速,轻便且兼容许多不同的环境。

没有自动类型分辨率让我感到困扰,所以我去寻找并找到了一些非常有趣的东西: Protobuf T4 TypeModel Generator 。 我还没有尝试过。 如果人们感兴趣,我可能会稍后再这样做,并使用更通用的解决方案更新答案。

如果您在使用它时遇到任何问题,请告诉我。

上述就是C#学习教程:.NET无法反序列化嵌套结构?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—猴子技术宅(www.ssfiction.com)

本文来自网络收集,不代表猴子技术宅立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ssfiction.com/ckf/1252560.html

(0)
上一篇 2022年11月15日 下午2:43
下一篇 2022年11月15日 下午2:45

精彩推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注