.NET无法反序列化嵌套的结构? [英] .NET can't deserialize nested structs?

查看:178
本文介绍了.NET无法反序列化嵌套的结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm running into issues getting C# (VS2008, Compact Framework, .NET is version 3.5 SP1) to successfully deserialize nested structs. The problem only appears in CF when I'm running on the emulator for the mobile device (I'm using the "Pocket PC 2003 Second Edition" emulator), the exact same code running on my Windows box does not have the same problem.

下面是我的代码:

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看起来很不错:

When I run this, the XML it creates looks good:

<?xml version="1.0" encoding="utf-16"?>
<Top xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Middle>
      <Freds>
         <Fred>
            <Name>Fred20</Name>
         </Fred>
         <Fred>
            <Name>Fred21</Name>
         </Fred>
      </Freds>
   </Middle>
   <Freds>
      <Fred>
         <Name>Fred10</Name>
      </Fred>
      <Fred>
         <Name>Fred11</Name>
      </Fred>
   </Freds>
</Top>



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

but C# is unable to successfully deserialize this XML - the console output is this:

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

XSD也有类似的问题:

xsd has similar problems:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Top" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="Top" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
 <xs:choice minOccurs="0" maxOccurs="unbounded">
  <xs:element name="Middle">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Freds" minOccurs="0" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="Fred" minOccurs="0" maxOccurs="unbounded">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="Name" type="xs:string" minOccurs="0" />
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
 </xs:element>
</xs:schema>

让我正好遇到一个C#的错误? ?还是我缺少明显的东西。

Have I just encountered a C# bug? Or am I missing something obvious?

请注意:这不是使用名称的问题两次,如果我创建了一个名为乔治结构是相同的弗雷德和改变。公共乔治[]乔治中学的内容,问题是没有任何好转。

Note: It's not a problem with using the name twice, if I create a struct named George that is identical to Fred, and change the contents of Middle to public George[] George, the problem isn't any better.

推荐答案

好吧,我给它一个镜头。

TLDR (for the skimmers): This post consist of two parts.

Part 1: A quick intro to Protobuf. Attributes are used here.

Part 2: The actual answer to the question: to configure serialization without modifying the inherited library

Ok, I'll give it a shot.

这个问题似乎是,你使用Compact Framework的,它不具有相同的序列化/反序列化能力,完整的.NET框架。因此,我们需要在这里一些自定义的序列化。

The problem seems to be that you're using the Compact Framework, which does not have the same serialization/deserialization capabilities as the full .NET framework. So we need some custom serialization here.

与契约框架的理念去,我的猜测是,你还想要的东西,表现良好,并具有占地面积小。于是我拿起的Protobuf 的任务(这也是的约12比XmlSerializer的倍的速度)

Going with the philosophy of the Compact Framework, my guess is that you also want something that performs well and has a small footprint. So I picked Protobuf for the task (which is also approximately 12 times faster than XmlSerializer)

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

You can install it by running this command:

Install-Package protobuf-net

让我们先从简单的方法 - 通过添加属性模型。无属性
配置随之而来的,正如你指出,原有的模式不能/不应该修改。这只是为了说明

Let's start with the easy way - by adding attributes to your model. Configuration without attributes comes next, as you pointed out that the original model can't/shouldn't be modified. This is only for illustration.

具有适当属性的装饰,你的模型应该是这样的:

Decorated with the appropriate attributes, your model would look like this:

我再说一遍,这部分仅用于说明目的 - 阅读配置无属性的

[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的方式做到这一点。您只需一个唯一的整数值分配到同一类中的每个成员,最大功告成有时代。

The only thing to note here is the usage of numbered members, called keys. It's essentially the same thing as giving them property names in the case of JSON or XML serialization, except this is the protobuf way to do it. You simply assign a unique integer value to each member within the same class, and most of the times you're done there.

为了方便起见,让我们添加一个简单的生成器从哪个我们可以实例化一个这是一个类似于在你的例子:

For convenience let's add a simple Builder from which we can instantiate a Top which is similar to the one in your example:

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;
    }
}

我们可以的连载的它是这样的:

We can serialize it like this:

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: "\nDC4\n\b\nACKFred20\n\b\nACKFred21DC2\b\nACKFred10DC2\b\nACKFred11"

反序列化的这样的:

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

    topOut = Protobuf.Serializer.Deserialize<Top>(stream);
}



正如你可以看到有管道为<$ C一点点$ C> MemoryStreams ,但除此之外,它应该很熟悉如何其他类型的序列化的工作。同样的,一切都可以一样好被配置自定义 TypeModel ,允许序列从模型是两个完全分开完成。

As you can see there is a little bit of plumbing for the MemoryStreams, but other than that it should look familiar to how other types of serialization work. Similarly, everything can just as well be accomplished by configuring a custom TypeModel, allowing serialization to be fully decoupled from the model.

在默认情况下,的Protobuf使用属性来定义 TypeModel ,然后存储在 ProtoBuf.Meta.RuntimeTypeModel.Default 。使用此属性,当你直接调用静态 Protobuf.Serializer。
我们还可以定义我们自己。它采取了一些摆弄周围(自我提醒:RTFM)得到它的工作,但它竟然是几乎一样简单:

By default, Protobuf uses the attributes to define the TypeModel and then stores it in ProtoBuf.Meta.RuntimeTypeModel.Default. This property is used when you call the static Protobuf.Serializer directly. We can also define our own. It took some fiddling around (note to self: RTFM) to get it working but it turned out to be almost as simple:

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[]));

现在我们所要做的就是改变一行代码两种功能:

Now all we have to do is change one line of code for both functions:

序列化:

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();
}



反序列化:

Deserialize:

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));
}

和它的工作原理是一样的。也许加个班,以保持组织的事情 - 给它两个公共方法序列化反序列化,和一个私有方法 BuildTypeModel (从在串行场构造和店铺call?)

And it works just the same. Perhaps add a class to keep things organized - give it two public methods Serialize and Deserialize, and a private method BuildTypeModel (to call from the constructor and store in a field on the serializer?)

您调用代码最终会寻找是这样的:

Your calling code would end up looking something like this:

var serializer = new CustomProtoBufSerializer();

var serialized = serializer.Serialize(someClassInput);

SomeClass someClassOutput = serializer.Deserialize(serialized);

有一件事情很快就清楚不过 - 的Protobuf尚未尽可能彻底文档和测试为最JSON和XML序列化在那里。此,与序列结果是非可读人类沿,可以在某些情况下的缺点。除此之外,它看起来像它的快速,轻巧,兼容多种不同的环境。

One thing quickly became clear though - Protobuf hasn't been as thoroughly documented and tested as most JSON and XML serializers out there. This, along with the serialization results being non-readable to humans, could be a drawback in some situations. Other than that it seems like it's fast, lightweight and compatible with many different environments.

由于没有自动类型分辨率困扰着我一下,让我去寻找和发现了什么这似乎很有趣:的Protobuf T4 TypeModel发电机。我一直没能去尝试它。如果有人有兴趣,我可以做,后来和更新一个更通用的解决方案了答案。

The absence of automatic type resolution bothered me a bit, so I went looking and found something that seems pretty interesting: Protobuf T4 TypeModel Generator. I haven't been able to try it yet. If people are interested, I might do that later and update the answer with a more generic solution.

让我知道如果您有任何麻烦,得到它的工作。

Let me know if you have any trouble getting it to work.

这篇关于.NET无法反序列化嵌套的结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆