NullReferenceException while using XElement

2019-09-04 14:45发布

问题:

I have a method that is suppose to edit a xml file:

    public void EditItem(Item item, string xml)
    {
        Data = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Items/" + xml + ".xml"));

        XElement node = Data.Root.Elements("item").Where(i => (string)i.Element("ID") == item.ID).FirstOrDefault();

        node.SetElementValue("ID", item.ID);
        node.SetElementValue("Name", item.Name);
        node.SetElementValue("Type", item.Type);
        node.SetElementValue("Kr", item.Kr);
        node.SetElementValue("Euro", item.Euro);

        Data.Save(HttpContext.Current.Server.MapPath("~/App_Data/Tables/" + xml + ".xml"));
    }

I get this validation error thru a try/catch in my controller: "Object reference not set to an instance of an object." Through some debugging, I found that "node" is null, even though "Data" contains all the right data from the xml, and the model.ID is correct.

the wierd thing is, that I have it working in another repo where the xml isnt dynamic, and the XDocument obj is loaded in the constructor.

Any ideas what causes it? Or maybe some ideas on a workaround.

Update. Xml snippet:

<?xml version="1.0" encoding="utf-8"?>
    <catagory id="0">
      <module>
        <item>
          <ID>101</ID>
          <Name>ClassicoTable(35x100x100)</Name>
          <Type>Model</Type>
          <Kr>0</Kr>
          <Euro>0</Euro>
          <DataType>ClassicoTableA</DataType>
        </item>
        <item>
          <ID>100</ID>
          <Name>ClassicoTable(102x100x140)</Name>
          <Type>Model</Type>
          <Kr>0</Kr>
          <Euro>0</Euro>
          <DataType>ClassicoTableB</DataType>
        </item> 



       ......

      </module>
    </catagory id="0">

回答1:

Should this line:

XElement node = Data.Root.Elements("item").Where(i => (string)i.Element("ID") == table.ID).FirstOrDefault();

be this (not sure where table.ID comes from):

XElement node = Data.Root.Elements("item").Where(i => (string)i.Element("ID") == item.ID).FirstOrDefault();

I would also check if node is null:

public void EditItem(Item item, string xml)
{
    Data = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Items/" + xml + ".xml"));

    XElement node = Data.Root.Elements("item").Where(i => (string)i.Element("ID") == item.ID).FirstOrDefault();

    if (node != null)
    {
        node.SetElementValue("ID", item.ID);
        node.SetElementValue("Name", item.Name);
        node.SetElementValue("Type", item.Type);
        node.SetElementValue("Kr", item.Kr);
        node.SetElementValue("Euro", item.Euro);

        Data.Save(HttpContext.Current.Server.MapPath("~/App_Data/Tables/" + xml + ".xml"));
    }
}


回答2:

Okay found a solution. I started wondering why @Kim wanted a snippet from my xml. So it made me think that Data.Root.Element() maybe wasnt the right way. So I tried with Descendants() instead, and that actually works. Why? I have no clue. Heres why:

In another repo I have the XDocument.load() in the constructor of the repo. I thought that would be good, because then I wouldnt have to repeat the same code in all the CRUD methods. But, as I wanted the xml dynamic, and the constructor doesnt accept parameters, I thought this way (original question) would be fine. Here the code from the "static" repo:

//Constructor
public CubeRepository()
{
    allCubes = new List<Cube>();

    CubeData = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Cubes/Cubep10p11.xml"));
    var cubes = from cube in CubeData.Descendants("item")
                select new Cube(cube.Element("ID").Value, 
                    cube.Element("Name").Value, 
                    cube.Element("Type").Value,
                    (int)cube.Element("Kr"),
                    (int)cube.Element("Euro"));

    allCubes.AddRange(cubes.ToList<Cube>());
}

And in my edit method of that repo:

public void EditCube(Cube cube)
{
    XElement node = CubeData.Root.Elements("item").Where(i => (string)i.Element("ID") == cube.ID).FirstOrDefault();
    node.SetElementValue("ID", cube.ID);
    node.SetElementValue("Name", cube.Name);
    node.SetElementValue("Type", cube.Name);
    node.SetElementValue("Kr", cube.Kr);
    node.SetElementValue("Euro", cube.Euro);

    CubeData.Save(HttpContext.Current.Server.MapPath("~/App_Data/Cubes/Cubep10p11.xml"));
}

And that works like I charm, even though im using CubeData.Root.Elements("item"). Note: Elements insted of Descendants. The structure of the xml files are identical.