7

I'm trying to read in element values from a simple XML doc and bind these to an object however I'm running into problems with my XML document. I have validated it and can confirm there are no issues with the document itself however expanding the results on the line:

var nodes = from xDoc in xml.Descendants("RewriteRule")
                select xmlSerializer.Deserialize(xml.CreateReader()) as Url;

Show "There is an error in XML document (0, 0)"

The inner exceptions reads <RewriteRules xmlns=''> was not expected.

I'm not sure what I'm doing wrong here?

My XML is below:

<?xml version="1.0" encoding="utf-8" ?>
<RewriteRules>
    <RewriteRule>
        <From>fromurl</From>
        <To>tourl</To>
        <Type>301</Type>
    </RewriteRule>
</RewriteRules>

The code that loads the XML file and attempts to de-serialize it: -

public static UrlCollection GetRewriteXML(string fileName)
{
    XDocument xml = XDocument.Load(HttpContext.Current.Server.MapPath(fileName));
    var xmlSerializer = new XmlSerializer(typeof(Url));

    var nodes = from xDoc in xml.Descendants("RewriteRule")
                select xmlSerializer.Deserialize(xml.CreateReader()) as Url;

    return nodes as UrlCollection;
}

My Url object class: -

[Serializable]
[XmlRoot("RewriteRule")]
public class Url
{
    [XmlElement("From")]
    public string From { get; set; }
    [XmlElement("To")]
    public string To { get; set; }
    [XmlElement("Type")]
    public string StatusCode { get; set; }

    public Url()
    {
    }

    public Url(Url url)
    {
        url.From = this.From;
        url.To = this.To;
        url.StatusCode = this.StatusCode;
    }
}

Can anyone see what I'm doing wrong here?

Thanks

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
DGibbs
  • 14,316
  • 7
  • 44
  • 83

3 Answers3

4

I'm not too familiar with the from select statement, but it seems you just pass in xml which is the whole XDocument, instead of the XElement that is your RewriteRule. That is why you get the error message that RewriteRules is unknown - the XmlSerializer expects a single RewriteRule.

I managed to rewrite your code using LINQ instead (but if you know how to get the single element from the from select statement, that should work equally well).

This should give you the correct result - rr is the XElement that is returned from Descendants:

public static IEnumerable<Url> GetRewriteXML()
{
    XDocument xml = XDocument.Load(HttpContext.Current.Server.MapPath(fileName));

    var xmlSerializer = new XmlSerializer(typeof(Url));

    var nodes = xml.Descendants("RewriteRule")
                .Select(rr => xmlSerializer.Deserialize(rr.CreateReader()) as Url);

    return nodes;
}
default
  • 11,485
  • 9
  • 66
  • 102
  • maybe it's `xDoc`? so `select xmlSerializer.Deserialize(xDoc.CreateReader())`? – default Mar 15 '13 at 09:21
  • Yes the element would be `xDoc`, didn't think to try CreateReader on the element itself, i'll give this a quick test – DGibbs Mar 15 '13 at 09:27
  • Yep, that was all i was missing. Changed `xml.CreateReader()` to `xDoc.CreateReader()`. Works perfectly now, thanks! – DGibbs Mar 15 '13 at 09:29
3

EDIT: The name of your Url class does not match. You need to rename it to "RewriteRule"or define it this way:

[Serializable]
[System.Xml.Serialization.XmlRoot("RewriteRule")]
public class Url
{
    [XmlElement("From")]
    public string From { get; set; }
    [XmlElement("To")]
    public string To { get; set; }
    [XmlElement("Type")]
    public string StatusCode { get; set; }

    public Url()
    {
    }
    public Url(Url url)
    {
        url.From = this.From;
        url.To = this.To;
        url.StatusCode = this.StatusCode;
    }
}
Patrick
  • 907
  • 9
  • 22
  • Hi @Patrick, I've tried adding your `RewriteRules` class and modifying my method but i still receive the same error. Curiously, if I modify my `Url` class `XmlRoot` attribute to be `RewriteRules` instead of `RewriteRule` the deserializer doesn't throw an error but the values bound to my object are all `null` so there is still something very strange going on – DGibbs Mar 14 '13 at 10:14
  • This is strange. I did setup a test and it has worked (I also used your code above with deserialze call). Does your editor provide correct xml files? I use notepad++. Not that the xml file is the problem. – Patrick Mar 18 '13 at 11:34
  • I created the xml doc via VS and used the w3c [validator](http://validator.w3.org) to check it – DGibbs Mar 18 '13 at 11:45
1

my approach is more used, when you deserialize it directly to an instance of a class.

If you want to use XDocument you may just write it like this. I do not use XDocument in my code. Since I need to deserialize the full xml packages I do it directly with deserialize of the root node. Therefore, I have proposed to the the root node to the right place in the previous post.

With XDocument you can access subparts directly. Here is working code for your purpose, but there may be others who can help you setting this code up more elegant:

public static UrlCollection GetRewriteXML(string fileName)
    {
        XDocument xml = XDocument.Load(HttpContext.Current.Server.MapPath(fileName));
        var urls = from s in xml.Descendants("RewriteRule")
                   select new
                   {
                       From = (string)s.Element("From").Value,
                       To = (string)s.Element("To").Value,
                       StatusCode = (string)s.Element("Type").Value
                   };
        UrlCollection nodes = new UrlCollection();
        foreach (var url in urls)
        {
            nodes.Add(new Url(url.From, url.To, url.StatusCode));
        }
        return nodes;
    }

[Serializable]
public class Url
{
    [XmlElement("From")]
    public string From { get; set; }
    [XmlElement("To")]
    public string To { get; set; }
    [XmlElement("Type")]
    public string StatusCode { get; set; }

    public Url()
    {
    }

    public Url(string From, string To, string StatusCode)
    {
        this.From = From;
        this.To = To;
        this.StatusCode = StatusCode;
    }

    public Url(Url url)
    {
        url.From = this.From;
        url.To = this.To;
        url.StatusCode = this.StatusCode;
    }
}
Patrick
  • 907
  • 9
  • 22
  • This worked thanks, confused as to why using anonymous type version worked whereas before it was returning null values for the properties. Can you explain? Instead of iterating over the urls i created a new constructor in my `UrlCollection` class that takes a dynamic collection and created a new url from that e.g. `public UrlCollection(IEnumerable urls){ this.Items.Clear(); foreach (dynamic item in urls) { Url url = new Url(item.From, item.To, item.StatusCode); this.Add(url); }}`. P.s you can always edit an existing answer ;) – DGibbs Mar 14 '13 at 13:48
  • Hi, it seems the serializer dislikes the name of your class Url. You need to rename it to "RewriteRule" or add the line: [Serializable] [System.Xml.Serialization.XmlRoot("RewriteRule")] public class Url – Patrick Mar 15 '13 at 08:30