2013-07-15 11 views
8

Ho diversi file XML con diversi elementi radice, ma lo stesso tipo di elementi figlio e vorrei poter creare una singola classe per contenere i diversi elementi radice e un'altra tenere ogni elemento figlio. Ecco due esempi di come sono i file XML.(De) serializzazione di diversi nomi di elementi radice utilizzando una classe

File 1:

<?xml version="1.0" encoding="utf-8" ?> 
<Sandra> 
    <Address> 
    <Street></Street> 
    <Number></Number> 
    </Address> 
</Sandra> 

File 2:

<?xml version="1.0" encoding="utf-8" ?> 
<John> 
    <Address> 
    <Street></Street> 
    <Number></Number> 
    </Address> 
</John> 

voglio essere in grado sia di serializzare e deserializzare questo usando solo 2 classi, come:

[Serializable] 
[XmlRoot] 
public class Person 
{ 
    [XmlElement("Address")] 
    public List<Address> Adresses { get; set; } 
} 

[Serializable] 
public class Address 
{ 
    public string Street { get; set; } 

    public string Number { get; set; } 
} 

I ho provato a leggerli usando:

var ser = new XmlSerializer(typeof(Person)); 
    var reader = XmlReader.Create("person1.xml"); 
    var person = (Person)ser.Deserialize(reader); 

ma ho "There is an error in XML document (2, 2).", perché il deserializzatore si aspettava un elemento <"Person"> radice e non un <"John"> o <"Paul">. Funziona bene se cambio [XmlRoot] a [XmlRoot("John")] ma è esattamente quello che sto cercando di evitare qui.

Inoltre, devo essere in grado di serializzare nuovamente utilizzando la stessa strana struttura XML, quindi ho bisogno di memorizzare il nome dell'elemento radice all'interno della classe Person.

So che potrei semplicemente creare il mio (de) serializzatore ma vorrei sapere se è possibile raggiungerlo utilizzando i metodi esistenti.

Modifica 1: (riavvolto).

Modifica 2: ripristinato le modifiche di "Modifica 1" da quando ho trovato un modo più semplice per ottenere ciò di cui avevo bisogno. Vedi la mia risposta qui sotto.

risposta

8

trovato un modo pulito e veloce per risolvere il mio problema! Ho appena dovuto utilizzare un XmlRootAttribute durante l'istanziazione di XmlSerializer. In questo modo posso impostare il nome dell'elemento root in fase di runtime.

var personsName = "Sandra"; 
var ser = new XmlSerializer(typeof(Person), 
    new XmlRootAttribute { ElementName = personsName }); 
var reader = XmlReader.Create("person1.xml"); 
var person = (Person)ser.Deserialize(reader); 

Ovviamente funziona anche allo stesso modo se voglio serializzarlo.

2

L'utilizzo dei nomi di persone come nomi di elementi XML sembra un po 'fastidioso. Cosa rappresenta ciascun elemento, un John o un Person?

Sarebbe meglio, se siete in grado di controllare la forma di quei file XML, per rappresentarli in questo modo:

<?xml version="1.0" encoding="utf-8" ?> 
<Person name="Sandra"> 
    <Address> 
    <Street></Street> 
    <Number></Number> 
    </Address> 
</Person> 

E poi avrete un modo semplice per memorizzare il nome in una proprietà, mappata come un attributo XML:

[Serializable] 
[XmlRoot] 
public class Person 
{ 
    [XmlElement("Address")] 
    public List<Address> Adresses { get; set; } 

    [XmlAttribute("name")] 
    public string Name { get; set;} 
} 
+1

Sì, è un po 'strano, anzi. Ma non spetta a me cambiare la struttura XML, devo solo leggere e scrivere rispettando questo formato. –

1

Implementare IXmlSerializable nella classe e fare la serializzazione nel modo desiderato nelle funzioni:

ReadXml(System.Xml.XmlReader reader) e 012.

esempio:

[Serializable] 
public class Threshold : IXmlSerializable 
{ 

public int Type {get;set;} 
public object Value {get;set;} 
public string Name {get;set;} 
public void ReadXml(System.Xml.XmlReader reader) 
{ 


XElement thresholdXML = XElement.Load(reader); 

if (!thresholdXML.HasElements || thresholdXML.IsEmpty) 
return; 

Type = (ThresholdType)int.Parse(thresholdXML.Element("Type").Value); 
Value = Type.Equals(ThresholdType.Complex) ? thresholdXML.Element("Value").Value : (object)Decimal.Parse(thresholdXML.Element("Value").Value); 
Name = thresholdXML.Element("Name").Value; 


} 


public System.Xml.Schema.XmlSchema GetSchema() 
{ 
return null; 
} 

public void WriteXml(System.Xml.XmlWriter writer) 
{ 
XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces(); 
xmlnsEmpty.Add("", ""); 


writer.WriteElementString("Type", ((int)Type).ToString("D")); 
writer.WriteElementString("Value", Value.ToString()); 
writer.WriteElementString("Name", Name); 
} 
} 
+0

Ho provato ad implementare '' IXmlSerializable'', ma quando ho provato a Deserializzare il primo archivio ho ottenuto lo stesso errore '' {{ non era previsto. "}' '. Il metodo ReadXml() della classe non è stato nemmeno chiamato. –

+0

Prima di tutto non sono necessari attributi [XmlRoot] o [XmlElement]. Prova anche a cancellare il tuo XML esistente e serializzarne di nuovi. Posizionare un punto di interruzione su ReadXML() e WriteXML() per vedere se vengono chiamati. Segui il debugger e aggiornami con i dettagli dell'eccezione e dove si è verificato. –

+0

Ha fatto ciò che mi hai detto di fare e ha ottenuto lo stesso errore quando ho provato a deserializzare: '' {" non era previsto."} ''. ReadXML non viene ancora chiamato, probabilmente perché il deserializer si aspetta che il nome della classe abbia lo stesso nome dell'elemento root ** o ** che ci sia un attributo '' [XmlRoot ("rootname")] ''. –

0

Utilizzare l'elemento radice come ArrayOfClassName e quindi provarlo.

<ArrayOfAlarmSummary> 
    <AlarmSummary> 
    <ClientId>1</ClientId> 
    <StationId>2</StationId> 
    <StationName>Station-2</StationName> 
    <DateTime>13/09/15</DateTime> 
    <TagName>AI2_2</TagName> 
    <Description>TR DC Current</Description> 
    <Units>Amps</Units> 
    <NormalOperation>10 to 100</NormalOperation> 
    <AlarmValue>132.48</AlarmValue> 
    <AlarmDescription> 
    </AlarmDescription> 
    </AlarmSummary> 
    <AlarmSummary> 
    <ClientId>1</ClientId> 
    <StationId>2</StationId> 
    <StationName>Station-2</StationName> 
    <DateTime>13/09/15</DateTime> 
    <TagName>AI2_2</TagName> 
    <Description>TR AC Current</Description> 
    <Units>Amps</Units> 
    <NormalOperation>10 to 100</NormalOperation> 
    <AlarmValue>132.48</AlarmValue> 
    <AlarmDescription> 
    </AlarmDescription> 
    </AlarmSummary> 
</ArrayOfAlarmSummary> 

Qui è la classe deserializzare, usando che si può deserializzare il file XML.

public class SerializeDeserialize<T> 
    { 
     StringBuilder sbData; 
     StringWriter swWriter; 
     XmlDocument xDoc; 
     XmlNodeReader xNodeReader; 
     XmlSerializer xmlSerializer; 
     public SerializeDeserialize() 
     { 
      sbData = new StringBuilder(); 
     } 
     public string SerializeData(T data) 
     { 
      XmlSerializer employeeSerializer = new XmlSerializer(typeof(T)); 
      swWriter = new StringWriter(sbData); 
      employeeSerializer.Serialize(swWriter, data); 
      return sbData.ToString(); 
     } 

     public T DeserializeData(string dataXML) 
     { 
      xDoc = new XmlDocument(); 
      xDoc.LoadXml(dataXML); 
      xNodeReader = new XmlNodeReader(xDoc.DocumentElement); 
      xmlSerializer = new XmlSerializer(typeof(T)); 
      var employeeData = xmlSerializer.Deserialize(xNodeReader); 
      T deserializedEmployee = (T)employeeData; 
      return deserializedEmployee; 
     } 
    } 

chiamare la classe

var appDomain = System.IO.Directory.GetCurrentDirectory() + "\\AlarmFiles"; 
      var strPath = Path.Combine(appDomain, 
       "Alarm_" + DateTime.Now.ToString("ddMMyyyy") + Constants.FileType.XmlFile); 
      var fi = new FileInfo(strPath); 
      if (fi.Exists) 
      { 
       try 
       { 
        var xmlString = System.IO.File.ReadAllText(strPath); 
        var serializeAlarmSummary = 
         new SerializeDeserialize<List<AlarmSummary>>(); 
        return serializeAlarmSummary.DeserializeData(xmlString); 
       } 
       catch (Exception ex) 
       { 
        throw ex; 
       } 

      }