【问题标题】:WCF XMLserialization of nested composite objects嵌套复合对象的 WCF XML 序列化
【发布时间】:2011-10-03 17:56:03
【问题描述】:

您好,我有一个服务合同,在其实施过程中,我试图将它收到的请求参数写入一个文本文件。此处将“TypeA a”的复合对象作为参数传递。

        namespace WebService
{
    // NOTE: If you change the interface name "IRequestStatusChanged" here, you must also update the reference to "IRequestStatusChanged" in App.config.
    [ServiceContract]
    public interface IRequestStatusChanged
    {
        [OperationContract]
        Input StatusChanged(Input In);

    }

    [Serializable] [DataContract]
    public class Input
    {
        [DataMember]
        RequestStatus RS = new RequestStatus();

    }

    [Serializable] [DataContract]
    public class RequestStatus
    {
        [DataMember]
        RequestToken RT = new RequestToken();

        [DataMember]
        public String State

    }
    [Serializable] [DataContract]
    public class RequestToken
    {
        [DataMember]
        public string Id;
    }
}

Implementation of contract

namespace WebService
{
    // NOTE: If you change the class name "RequestStatusChanged" here, you must also update the reference to "RequestStatusChanged" in App.config.
    public class RequestStatusChanged : IRequestStatusChanged
    {
        public Input StatusChanged(Input In)
        {


           /* IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            Stream outfile = new FileStream(@"C:\test.txt", FileMode.Open,FileAccess.Write);
            formatter.Serialize(outfile, In);
            outfile.Close(); */


            XmlSerializer serializer = new XmlSerializer(typeof(Input));
            TextWriter TW = new StreamWriter(@"c:\test.xml");
            serializer.Serialize(TW, In);
            TW.Close();

            return In;
        }

    }
}

之前我尝试 IFORMATTER 序列化对象并将其写入文本文件,但它不是人类可读的,所以我尝试了 xml 序列化。

当我检查写入的 XML 文件时,它只有“TypeA”对象的标签,而没有将“TypeB”和“TypeC”对象写入文件。我已在服务合同中将所有类标记为 [Serializable]。我想将收到的所有文本或 xml 格式的参数写入一个文件,以便它们可读(一种日志文件)。

【问题讨论】:

  • 当你的客户端发送TypeA对象时,它是否为TypeB的TypeC设置了一个值?如果它们都为空,则序列化程序可能不会将它们提交到文件中。

标签: xml wcf serialization object composite


【解决方案1】:

XmlSerializer 仅序列化 public 成员。您定义的DataContract 类型中的所有成员都没有任何访问说明符,因此它们默认为私有。

但是,您在这里使用了两个不同的序列化框架 - DataContractSerializer 被 WCF 隐式使用,除非您另有说明,而 XmlSerializer。为什么不直接使用DataContractSerializer,因为您已经具备了 WCF 所需的所有属性?

var serialiser = new DataContractSerializer(typeof(TypeA));
serialiser.WriteObject(xmlWriter, typeAObj);

【讨论】:

    【解决方案2】:

    类型 'TypeB' 和 'TypeC' 不会写入文件,因为它们不是必需的 - 当序列化程序序列化或反序列化 TypeA 的实例时,它知道成员 'b' 的类型是TypeB,所以不写类型信息。你也需要这些信息吗?由于您正在序列化 TypeA,并且您知道字段 b 的类型为 TypeB,因此您不需要日志文件中的额外信息。

    在 WCF 使用的所有序列化器中,唯一可以“强制”一直写入类型的序列化器是 JSON 序列化器 - 但是你会得到 JSON 的结果,而不是 XML - 见下文。

    public class StackOverflow_6666697
    {
        [DataContract]
        public class TypeA { [DataMember] public TypeB b = new TypeB(); }
        [DataContract]
        public class TypeB { [DataMember] public TypeC c = new TypeC(); }
        [DataContract]
        public class TypeC { [DataMember] public string S1; }
        public static void Test()
        {
            MemoryStream ms = new MemoryStream();
            XmlWriterSettings ws = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = "  ",
                OmitXmlDeclaration = true,
                Encoding = Encoding.UTF8
            };
            TypeA instance = new TypeA { b = new TypeB { c = new TypeC { S1 = "Hello world" } } };
    
            XmlWriter w = XmlWriter.Create(ms, ws);
            new XmlSerializer(typeof(TypeA)).Serialize(w, instance);
            w.Flush();
            Console.WriteLine("XmlSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
    
            ms.SetLength(0);
            w = XmlWriter.Create(ms, ws);
            new DataContractSerializer(typeof(TypeA)).WriteObject(w, instance);
            w.Flush();
            Console.WriteLine("DataContractSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
    
            ms.SetLength(0);
            w = XmlWriter.Create(ms, ws);
            new NetDataContractSerializer().WriteObject(w, instance);
            w.Flush();
            Console.WriteLine("NetDataContractSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
    
            ms.SetLength(0);
            w = JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.UTF8);
            new DataContractJsonSerializer(typeof(TypeA), null, 65536, false, null, true).WriteObject(w, instance);
            w.Flush();
            Console.WriteLine("DataContractJsonSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
        }
    }
    

    另一种选择(我真的不建议这样做,因为您会为了记录而更改系统的逻辑)是将字段声明为object。这样,由于声明的类型与实际类型不同,类型信息将被写出 - 见下文。

    public class StackOverflow_6666697
    {
        [DataContract]
        public class TypeA { [DataMember] public object b = new TypeB(); }
        [DataContract]
        public class TypeB { [DataMember] public object c = new TypeC(); }
        [DataContract]
        public class TypeC { [DataMember] public string S1; }
        public static void Test()
        {
            MemoryStream ms = new MemoryStream();
            XmlWriterSettings ws = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = "  ",
                OmitXmlDeclaration = true,
                Encoding = Encoding.UTF8
            };
            TypeA instance = new TypeA { b = new TypeB { c = new TypeC { S1 = "Hello world" } } };
    
            XmlWriter w = XmlWriter.Create(ms, ws);
            new XmlSerializer(typeof(TypeA), new Type[] { typeof(TypeB), typeof(TypeC) }).Serialize(w, instance);
            w.Flush();
            Console.WriteLine("XmlSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
    
            ms.SetLength(0);
            w = XmlWriter.Create(ms, ws);
            new DataContractSerializer(typeof(TypeA), new Type[] { typeof(TypeB), typeof(TypeC) }).WriteObject(w, instance);
            w.Flush();
            Console.WriteLine("DataContractSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
    
            ms.SetLength(0);
            w = XmlWriter.Create(ms, ws);
            new NetDataContractSerializer().WriteObject(w, instance);
            w.Flush();
            Console.WriteLine("NetDataContractSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
    
            ms.SetLength(0);
            w = JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.UTF8);
            new DataContractJsonSerializer(typeof(TypeA), new Type[] { typeof(TypeB), typeof(TypeC) }, 65536, false, null, true).WriteObject(w, instance);
            w.Flush();
            Console.WriteLine("DataContractJsonSerializer:");
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
        }
    }
    

    【讨论】:

    • 非常感谢您提供的信息……真的很有帮助。请求以 XML 格式接收,因此我想将其写入 XML 或纯文本格式的文件,以便可读。是的,我还需要编写 TypeB 和 TypeC 对象。如果我能找到某种方法将整个复合对象写入文件,那就太好了。
    • 请求中是否包含您想要的类型信息?如果是这样,您可以考虑使用 MessageInspector (IDispatchMessageInspector) 来记录此时的请求。
    • 我在 OP 关于需要 type 信息的问题中看不到任何内容 - 只是没有写入嵌套值。那是因为XmlSerializer 不会接触非公开成员。
    • 我已经将成员声明为 public ,抱歉我在这里错过了。我猜根据上述信息 XMLseralizer 只是不会序列化嵌套对象。它只会序列化单个指定类型的对象。
    • @user832219,如果您将帖子中的代码声明为公开,我建议您更新它们。你能澄清你需要什么吗?您是否需要对 TypeB/TypeC 的 instances 进行序列化(如果它们是公共的,XmlSerializer 应该这样做),还是需要对它们的 类型名称 进行序列化除了他们的实例?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多