C# XML Serialisation van object

Pagina: 1
Acties:

Onderwerpen


Verwijderd

Topicstarter
Ik probeer een object te serializen, maar een van de elementen daarvan is een object waar verschillende types aan gekoppeld kunnen worden.

C#:
1
2
3
4
5
6
7
8
9
10
11
    [XmlInclude(typeof(GetChannels))]
    [XmlInclude(typeof(GetPrograms))]
    public class Body
    {
        public object body;
    }

[Serializable]
public class GetChannels
{
}


Als ik dit serialize krijg ik volgende output:
<Body><body xsi:type="GetChannels" /></Body>

Maar wat ik wil is het volgende:
<Body><GetChannels>..</GetChannels></Body>

Normaal kan dat met het attribute [XmlElement], maar hier is de naam dus dynamisch. Ik heb al verschillende zaken geprobeerd. Ook gegoogled, maar ik weet niet echt hoe ik dit moet opzoeken.

Wat ook handig zou zijn is dat de XmlInclude's niet nodig zouden zijn.

Kan iemand mij hier helpen?

[ Voor 9% gewijzigd door Verwijderd op 15-12-2011 11:21 ]


  • keesdewit
  • Registratie: December 2003
  • Laatst online: 19-06 20:46
Dit werkt denk ik wel voor je:

Instantiëren / gebruiken:
C#:
1
2
3
4
5
6
7
8
9
10
11
Body bdy = new Body();

bdy.GetPrograms = new GetPrograms();
bdy.GetChannels = new GetChannels();

System.Xml.Serialization.XmlSerializer x = 
new System.Xml.Serialization.XmlSerializer(bdy.GetType());

x.Serialize(Console.Out, bdy);

Console.ReadKey();


Class:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Xml.Serialization;
using System;

[XmlInclude(typeof(GetChannels))]
[XmlInclude(typeof(GetPrograms))]
public class Body
{
    public object GetChannels;
    public object GetPrograms;
}

[Serializable]
public class GetChannels
{
}

[Serializable]
public class GetPrograms
{
}


Output:
XML:
1
2
3
4
5
<?xml version="1.0" encoding="ibm850"?>
<Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <GetChannels xsi:type="GetChannels" />
  <GetPrograms xsi:type="GetPrograms" />
</Body>

[ Voor 3% gewijzigd door keesdewit op 15-12-2011 18:25 ]


  • pedorus
  • Registratie: Januari 2008
  • Niet online
Het makkelijkst lijkt me gewoon wat properties toevoegen, dat is vast ook handig in het programma zelf. Dus iets als:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Body
{
    [XmlIgnore]
    public object body { get; set; }

    [XmlElement("GetChannels")]
    public Channels Channels
    {
        get { return body as Channels; }
        set { body = value; }
    }

    [XmlElement("GetPrograms")]
    public Programs Programs
    {
        get { return body as Programs; }
        set { body = value; }
    }
}
Alternatief kun je MSDN: IXmlSerializable Interface implementeren.

Edit: ik vond toch dat dit wat moeilijk was. Na uitproberen blijkt dit ook gewoon te kunnen: :p
C#:
1
2
3
4
5
6
public class Body
{
    [XmlElement(ElementName = "GetChannels", Type = typeof(Channels)),
    XmlElement(ElementName = "GetPrograms", Type = typeof(Programs))]
    public object body { get; set; }
}

[ Voor 21% gewijzigd door pedorus op 15-12-2011 19:36 ]

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Verwijderd

Topicstarter
Dit kan inderdaad allemaal wel, maar dit doet niet echt wat ik wil.
Voor een project ben ik zelf een soap classe aan het bouwen.
Ik weet wel dat dit allemaal mooi al in .NET zit, maar het voldoet niet aan de manier waarop wij het gaan gebruiken.

Momenteel ziet m'n soap classe er zo uit: (wel wat gestript)
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[Serializable]
[XmlRoot(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
    [XmlElement("Header")]
    public Header header = new Header();
    [XmlElement("Body")]
    public Body body = new Body();

    public string Serialize()
    {
        XmlSerializer x = new XmlSerializer(this.GetType());

        StringWriter sw = new StringWriter();
 
        x.Serialize(sw, this);

        return sw.ToString();
    }

    public static Envelope Deserialize(string data)
    {
        XmlSerializer x = new XmlSerializer(typeof(Envelope));

        MemoryStream ms = new MemoryStream( Encoding.UTF8.GetBytes(data));

        StreamReader xr = new StreamReader(ms);

        return (Envelope)x.Deserialize(xr);
    }

    public class Header
    {
    }

    public class Body
    { 
        [XmlElement(Namespace = "http://test/")]
        public TVGuideEPG.GetChannels GetChannels;

        [XmlArray(Namespace = "http://test/")]
        public TVGuideEPG.GetChannelsResult[] GetChannelsResponse;
    }
}


Op zich doet dat zijn werk natuurlijk wel, maar ik zou liever hebben dat die Envelope classe wat generieker wordt, en dat er dus niet voor elk datatype die verstuurd wordt een nieuwe variabele aangemaakt moet worden.

De envelope class moet dus verder gebruikt kunnen worden, ook als er andere types bijkomen, zonder dat er iets aan veranderd moet worden. Dit zowel voor serialisation en deserialisation.

Het beste zou dus zijn dat de Body een object wordt, maar dan krijg ik problemen met de namen in de gegenereerde XML.

[ Voor 13% gewijzigd door Verwijderd op 15-12-2011 20:03 ]


  • pedorus
  • Registratie: Januari 2008
  • Niet online
Zowel XmlSerializer als DataContractSerializer hebben op de een of andere manier de types nodig. Ik denk dat Serialization for Rapid Application Development: A Better Approach meer is wat je zoekt dan. :p

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Verwijderd

Topicstarter
Dat XmlSerializer de types nodig heeft begrijp ik wel. Maar het moet toch mogelijk zijn om de Envelope class zo te maken dat ie bruikbaar is voor alle types van objecten. Want zoals het nu is heeft GetChannels en GetChannelsResponse totaal niets te maken met het doel van die klasse.

  • pedorus
  • Registratie: Januari 2008
  • Niet online
offtopic:
Je hebt niet met live-topic de edit van mijn eerste post overgeslagen? Je kan dus iig gewoon meerdere XmlElements gebruiken, hoewel het koppeling blijft.

Tsja, bij het wegschijven is het natuurlijk geen probleem om desnoods willekeurige klassen te serializen als je IXmlSerializable implementeerd:
C#:
1
2
3
4
5
6
7
8
    public void WriteXml(System.Xml.XmlWriter writer)
    {
        if (body == null)
            return;
        var ns = new XmlSerializerNamespaces();
        ns.Add("", "");
        new XmlSerializer(body.GetType()).Serialize(writer, body, ns);
    }

Lossere koppeling kan niet. Het probleem ontstaat enkel bij het deserializen (welke klassen zijn toegestaan/kunnen met welke naam voorkomen?). Daarvoor is bijvoorbeeld die CodeProject-oplossing. Er al altijd een manier van registreren moeten zijn, of je moet willekeurige .net-objecten gaan toestaan, wat mij altijd ongewenst lijkt (iig een security-probleem).

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten

Pagina: 1