WCF XML-сериализация вложенных составных объектов - PullRequest
2 голосов
/ 12 июля 2011

Привет, у меня есть контракт на обслуживание, и при его реализации я пытаюсь записать параметры запроса, которые он получает, в текстовом файле. Здесь составной объект типа 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» в файл. Я отметил все классы как [Сериализуемые] в сервисном контракте. Я хочу записать все параметры, полученные в текстовом или XML-формате, в файл, чтобы они были читабельными (вид файла журнала).

Ответы [ 2 ]

2 голосов
/ 12 июля 2011

Типы «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()));
    }
}
0 голосов
/ 12 июля 2011

XmlSerializer только сериализует публичные члены.Ни один из членов в определенных вами типах DataContract не имеет спецификаторов доступа, поэтому по умолчанию они имеют значение private.

Однако здесь вы используете две разные среды сериализации - DataContractSerializer неявно используетсяWCF, если вы не скажете иначе, и XmlSerializer.Почему бы просто не использовать DataContractSerializer, поскольку у вас уже есть все необходимые атрибуты для целей WCF?

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