2016-08-08 6 views
0

Ich versuche, XML-Antwort von Moodle Web Services zu deserialisieren.Parse moodle XML zu C# (dotnet) -Objekt

Ich könnte es in ein Dotnet-Objekt analysieren, wenn es verschiedene benannte Attribute wie ID, Kurzname, IDnumber usw. hatte. Aber es hat ein Array von KEY-Attributen mit tatsächlichen Feldnamen als Wert und darin befindet sich ein weiterer Knoten den Feldwert haben. Hier

ist ein Beispiel:

<?xml version="1.0" encoding="UTF-8" ?> 
     <RESPONSE> 
      <MULTIPLE> 
      <SINGLE> 
       <KEY name="id"> 
        <VALUE>2</VALUE> 
       </KEY> 
       <KEY name="shortname"> 
        <VALUE>CS-101</VALUE> 
       </KEY> 
       <KEY name="fullname"> 
        <VALUE>CS-101</VALUE> 
       </KEY> 
       <KEY name="enrolledusercount"> 
        <VALUE>2</VALUE> 
       </KEY> 
       <KEY name="idnumber"> 
        <VALUE></VALUE> 
       </KEY> 
       <KEY name="visible"> 
        <VALUE>1</VALUE> 
       </KEY> 
       <KEY name="summary"> 
        <VALUE>&lt;p&gt;CS-101&lt;br /&gt;&lt;/p&gt;</VALUE> 
       </KEY> 
       <KEY name="summaryformat"> 
        <VALUE>1</VALUE> 
       </KEY> 
       <KEY name="format"> 
        <VALUE>weeks</VALUE> 
       </KEY> 
       <KEY name="showgrades"> 
        <VALUE>1</VALUE> 
       </KEY> 
       <KEY name="lang"> 
        <VALUE></VALUE> 
       </KEY> 
       <KEY name="enablecompletion"> 
        <VALUE>0</VALUE> 
       </KEY> 
      </SINGLE> 
      </MULTIPLE> 
     </RESPONSE> 

Ich möchte diese XML in ein Objekt dieser Klasse analysieren:

class Course 
{ 
    public int id { get; set; } 
    public string shortname { get; set; } //short name of course 
    public string fullname { get; set; } //long name of course 
    public int enrolledusercount { get; set; } //Number of enrolled users in this course 
    public string idnumber { get; set; } //id number of course 
    public int visible { get; set; } //1 means visible, 0 means hidden course 
    public string summary { get; set; } 
    public int summaryformat { get; set; } //summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN) 
    public string format { get; set; } //course format: weeks, topics, social, site 
    public int showgrades { get; set; } //true if grades are shown, otherwise false 
    public string lang { get; set; } //forced course language 
    public int enablecompletion { get; set; } //true if completion is enabled, otherwise false 
} 

Gibt es einen direkten Weg, es zu tun, oder sollte ich einen Parser schreiben Methode mit Schalter Fällen für jedes Feld?

+1

Wäre es hilfreich, JSON-formatierte Daten anstelle von XML zurückzugeben? Das Hinzufügen des Parameters moodlewsrestformat = json zur Webservice-Anfrage erfolgt stattdessen im JSON-Format. – davosmith

+0

eigentlich wusste ich, dass es auch JSON zurückgibt, aber irgendwie habe ich es einfach vergessen und konnte nicht über das Standard-XML-Format hinaus denken. JSON wäre viel einfacher zu arbeiten. Vielen Dank. – Danish

Antwort

2

Sie müssen Ihren benutzerdefinierten Parser mit XmlReader schreiben, keinen Standard-Deserializer, der dies durch eine Voreinstellung tun könnte.

Auch müssen Sie nicht Schalter/Fälle verwenden, können Sie Reflection verwenden, um Ihre Requisiten zu füllen.

+0

Danke, das dachte ich mir auch, aber ich war einfach zu faul, Spiegelung zu benutzen :-) – Danish

0

Soweit ich weiß, gibt es keinen Standard-Deserializer für diese Struktur. Und wie @Nigrimmist sagte - Sie müssen nicht Schalter-Fall verwenden.

Sie können XmlReader zum Lesen eines XML verwenden, oder tun Sie es mit XmlDocument. Beispielcode für die Analyse unten. Ich habe es nicht getestet, also benutze es vorsichtig.

public T ConvertFromXml<T>(string yourXml, params object[] activationData) 
    where T : new() //if your type has default constructor 
{ 
    var resultType = typeof(T); 
    //if your type has not default constructor 
    var result = (T)Activator.CreateInstance(typeof(T), activationData); 
    //if it has default constructor 
    var result = new T(); 

    //create an instance of xml reader 
    var xmlDocument = new XmlDocument(); 
    xmlDocument.LoadXml(yourXml); 

    //expecting that your xml response will always have the same structure 
    if (xmlDocument.SelectSingleNode("RESPONSE/SINGLE") != null) 
    { 
     foreach(XmlNode node in xmlDocument.SelectNodes("RESPONSE/SINGLE/KEY")) 
     { 
      var prop = resultType.GetProperty(node.Attributes["name"].Value); 
      if (prop != null) 
      { 
       var value = prop.SelectSingleNode("Value").Value; 
       //if value does not exist - check if null value can be assigned 
       if (value == null && (!prop.PropertyType.IsValueType || (Nullable.GetUnderlyingType(prop.PropertyType) != null))) 
       { 
        prop.SetValue(result, value); //explicitly setting the required value 
       } 
       else if (value != null) 
       { 
        //we receiving the string, so for number parameters we should parse it 
        if (IsNumberType(prop.PropertyType)) 
        { 
         prop.SetValue(result, double.Parse(value)); 
        } 
        else 
        { 
         prop.SetValue(result, value); //will throw an exception if property type is not a string 
        } 

        //need some additional work for DateTime, TimeSpan, arrays and other 
       } 

      } 
      else 
      { 
       //remove next line if you do not need a validation for property presence 
       throw new ArgumentException("Could not find the required property " + node.Attributes["name"].Value); 
      } 
     } 
    } 
    else 
    { 
     throw new ArgumentException("Xml has invalid structure"); 
    } 

    return result; 
} 

private bool IsNumberType(Type t) 
{ 
    var numberTypes = new[] { 
     typeof(byte), typeof(short), typeof(int), typeof(long), 
     typeof(float), typeof(double), typeof(decimal), 
     typeof(byte?), typeof(short?), typeof(int?), typeof(long?), 
     typeof(float?), typeof(double?), typeof(decimal?) 
    }; 

    return numberTypes.Contains(t); 
} 

Und Probe Nutzung:

var xml = /*your method to get string representing a xml*/; 
return ConvertFromXml<Course>(xml); 

Wenn Sie Antwort in json Darstellung statt xml, werfen Sie einen Blick auf Json.Net Bibliothek bekommen können es einfach zu bedienen ist.

0

Verwenden Sie XML Linq. Siehe Code unten. Ich habe nur ID analysiert, aber Sie können die anderen Eigenschaften hinzufügen.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Xml; 
using System.Xml.Linq; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     const string FILENAME = @"c:\temp\test.xml"; 
     static void Main(string[] args) 
     { 
      Course course = new Course(); 
      course.ReadXML(FILENAME); 
     } 
    } 
    public class Course 
    { 
     public static List<Course> courses = new List<Course>(); 

     public int id { get; set; } 
     public string shortname { get; set; } //short name of course 
     public string fullname { get; set; } //long name of course 
     public int enrolledusercount { get; set; } //Number of enrolled users in this course 
     public string idnumber { get; set; } //id number of course 
     public int visible { get; set; } //1 means visible, 0 means hidden course 
     public string summary { get; set; } 
     public int summaryformat { get; set; } //summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN) 
     public string format { get; set; } //course format: weeks, topics, social, site 
     public int showgrades { get; set; } //true if grades are shown, otherwise false 
     public string lang { get; set; } //forced course language 
     public int enablecompletion { get; set; } //true if completion is enabled, otherwise false 

     public void ReadXML(string filename) 
     { 
      XDocument doc = XDocument.Load(filename); 
      courses = doc.Descendants("SINGLE").Select(x => ReadKeys(x)).ToList(); 
     } 
     public Course ReadKeys(XElement single) 
     { 
      Course newCourse = new Course(); 
      foreach(XElement key in single.Descendants("KEY")) 
      { 
       switch(key.Attribute("name").Value) 
       { 
        case "id" : 
         newCourse.id = (int)key.Element("VALUE"); 
         break; 
       } 
      } 
      return newCourse; 
     } 
    } 
}