C# .NET可以';t反序列化嵌套结构?

C# .NET可以';t反序列化嵌套结构?,c#,xml,serialization,compact-framework,C#,Xml,Serialization,Compact Framework,我在使用C#(VS2008,Compact Framework,.NET是版本3.5 SP1)成功反序列化嵌套结构时遇到了一些问题。只有当我在移动设备的模拟器上运行时(我使用的是“Pocket PC 2003第二版”模拟器),这个问题才会出现在CF中,在我的Windows box上运行的完全相同的代码没有相同的问题 这是我的密码: public struct Fred { public string Name; } public struct Middle { public

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

这是我的密码:

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

<?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>
xsd也有类似的问题:

<?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#bug吗?还是我遗漏了一些明显的东西

注意:两次使用这个名字并没有问题,如果我创建了一个与Fred相同的名为George的结构,并将Middle的内容更改为public George[]George,那么问题就不会更好了。

TLDR(对于浏览者):这篇文章由两部分组成。 第1部分:Protobuf的快速介绍。这里使用属性。 第2部分:问题的实际答案:在不修改继承库的情况下配置序列化 好的,我来试一试

问题似乎在于您使用的是Compact Framework,它没有与完整的.NET Framework相同的序列化/反序列化功能。所以我们需要一些自定义序列化

按照紧凑框架的理念,我的猜测是,您还需要性能良好且占用空间小的东西。所以我选择了这项任务(也是)

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

Install-Package protobuf-net
让我们从简单的方法开始——向模型添加属性。 接下来是没有属性的配置,正如您指出的,原始模型不能/不应该被修改。这只是为了举例说明

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

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);
}
var serializer = new CustomProtoBufSerializer();

var serialized = serializer.Serialize(someClassInput);

SomeClass someClassOutput = serializer.Deserialize(serialized);
第1部分:带属性的配置。 我重复一遍,本部分仅用于说明目的-请继续阅读“无属性配置”

这里唯一需要注意的是编号成员的用法,称为键。在JSON或XML序列化的情况下,这本质上与为它们指定属性名相同,只是这是protobuf的方法。您只需为同一类中的每个成员分配一个唯一的整数值,大多数情况下都是这样做的

为了方便起见,让我们添加一个简单的生成器,从中我们可以实例化一个类似于示例中的
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: "\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);
}
var serializer = new CustomProtoBufSerializer();

var serialized = serializer.Serialize(someClassInput);

SomeClass someClassOutput = serializer.Deserialize(serialized);
并将其反序列化如下:

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);
}
var serializer = new CustomProtoBufSerializer();

var serialized = serializer.Serialize(someClassInput);

SomeClass someClassOutput = serializer.Deserialize(serialized);
现在我们所要做的就是为这两个函数更改一行代码:

序列化:

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));
}
它的工作原理是一样的。也许可以添加一个类来保持事物的有序性-给它两个公共方法
序列化
反序列化
,以及一个私有方法
BuildTypeModel
(从构造函数调用并存储在序列化器上的字段中?)

您的呼叫代码最终会显示如下内容:

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);
}
var serializer = new CustomProtoBufSerializer();

var serialized = serializer.Serialize(someClassInput);

SomeClass someClassOutput = serializer.Deserialize(serialized);
但有一件事很快就变得清楚了——Protobuf并没有像大多数JSON和XML序列化程序那样被彻底记录和测试。这与序列化结果不可读一起,在某些情况下可能是一个缺点。除此之外,它看起来速度快、重量轻,并且与许多不同的环境兼容

自动类型解析的缺失让我有点困扰,所以我去寻找,发现了一些似乎很有趣的东西:。我还没试过。如果有人感兴趣,我可能稍后会这样做,并用更通用的解决方案更新答案


如果您在使其工作时遇到任何问题,请告诉我。

使用您的代码,我得到:go2.Freds[0]。名称为“Fred10”| go2.Middle.Freds[0]。名称为“Fred20”。您正在使用哪个版本的.NET进行开发?这里也无法使用.NET 3.5/VS 2008复制此版本。需要注意的一点是,不应该对XML使用
编码.ASCII
,因为这会去除国际字符。使用
Encoding.Unicode
Encoding.UTF8
代替,例如:
使用(MemoryStream ms=new MemoryStream(System.Text.Encoding.UTF8.GetBytes(xml)))使用(StreamReader sr=new StreamReader(ms,Encoding.UTF8)){}
我已经更新了我的帖子以包含版本信息,在.NET的compact framework版本中,这可能是一个缺点……首先,您的类型作为
struct
对象没有意义。它们不应该是可变的,它们不代表逻辑值,你对它们进行了大量的装箱,等等。它们应该只是类。是的,如果我创建这段代码,我会使用类。不幸的是,这是我继承的代码,它在一个库中-而对该库的更改不在当前项目的范围内,在库中打开引擎盖将显著影响范围和时间线…不幸的是,感谢您提供的大量信息(正如我在前面的评论中所说)我非常努力地避免对代码所在的库进行重大更改。如果我要“打开引擎盖”,我可能只是让它们都是类而不是结构(似乎避免了C#bug),没有必要对你的库进行任何修改——这毕竟是我回答的目的。否则,我还不如告诉您用JSON或XML序列化契约来装饰它们:)在我的帖子中提到这一部分:“接下来是没有属性的配置,正如您指出的,原始模型不能/不应该修改。这只是为了说明。*”或。。您的意思是说序列化/反序列化部分不起作用,也是库的一部分吗?因为在这种情况下,你可能运气不好,你无法与t合作