1

我有类似这样的 xml 代码:文件名为:player.xml

<root>
  <person>
    <fname>Dwight</fname>
    <lname>Howard</lname>
    <vertLeap>
      <try1>32.33</try1>
      <try2>33.33</try2>
      <try3>34.33</try3> 
    </vertLeap>
  </person>
  <person>
    <fname></fname>
    <lname>Jordan</lname>
    <vertLeap>
      <try1>40.33</try1> 
    </vertLeap>
  </person>
</root>

这不是我真正的 xml,但应该适用于该示例。现在我想使用 linq to xml 来读取数据。我正在尝试这样。

班级:

public class Player
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Leap1 { get; set; }
    public int Leap2 { get; set; }
    public int Leap3 { get; set; }

    public WritePlayertoDatabase()
    {
        //do stuff to write
    }
}

询问:

XDocument xDoc = XDocument.Load("player.xml");
var player_query = from p xDoc.Desendants("person")
                   select new Player
                   {
                       FirstName = p.Element("fname"),
                       LastName = p.Element("lname"),
                       Leap1 = p.Element("try1"),
                       Leap2 = p.Element("try2"),
                       Leap3 = p.Element("try3")
                   };

我收到 NullReferenceException。在我尝试使用该值之前,有没有办法测试元素是否存在?或者有没有更好的方法来实现这一点?

4

3 回答 3

4

因此,您的 linq 查询有一些问题。

1)p.Element("fname")将返回 fname XML 元素,而不是字符串。所以你仍然需要获取元素的值。类似地,Leap1-3 的属性是 int,但你会得到元素值作为搅拌,需要转换它。但是,try1-3 不是 xml 中的整数,因此您可能希望在 Player 类中将类型更改为其他类型。

2) try1 - tryx 元素都是 'vertleap' 的子元素。您不能直接从“人”中获取元素“try1”。它将为空。

那么更像这样的东西怎么样:

    var player_query = from p in xDoc.Descendants("person")
                       select new Player
                       {
                           FirstName =  p.Element("fname") != null ? p.Element("fname").Value : "",
                           LastName = p.Element("lname") != null ? p.Element("lname").Value : "",
                           Leap1 = p.Element("vertLeap") != null ? (p.Element("vertLeap").Element("try1") != null ? Decimal.Parse(p.Element("vertLeap").Element("try1").Value) : 0) : 0,

                           Leap2 = p.Element("vertLeap") != null ? (p.Element("vertLeap").Element("try2") != null ? Decimal.Parse(p.Element("vertLeap").Element("try2").Value) : 0) : 0,
                           Leap3 = p.Element("vertLeap") != null ? (p.Element("vertLeap").Element("try3") != null ? Decimal.Parse(p.Element("vertLeap").Element("try3").Value) : 0) : 0,
                       };
于 2013-02-15T00:32:05.783 回答
0

使用您发布的 XML,p.Element("try<X>")调用将始终返回 nulll ......正如我看到的 @SimonC 刚刚指出的那样。您需要沿着 XML 树向下隧道以获取值,或用于Descendants("try<x>").FirstOrDefault()获取具有匹配名称的第一个后代。这可能仍然为空,这使我们回到问题的真正重点。

问题实际上是您正在尝试对可能不存在的对象执行操作。除了@SimonC 建议的条件序列之外,您还可以使用辅助方法或扩展来检测缺失的元素并提供有意义的默认值。

public static string AsString(this XElement self)
{
    if (self == null)
        return null;
    return self.Value;
}

public static double AsDouble(this XElement self, double defValue = default(double))
{
    if (self == null)
        return defValue;
    double res = defValue;
    try { res = (double)self; }
    catch { }
    return res;
}

public static int AsInt(this XElement self, int defValue = default(int))
{
    if (self == null)
        return defValue;
    double res = defValue;
    try { res = (double)self; }
    catch { }
    return (int)res;
}

然后您的 Linq 查询变为:

var player_query = from p in xDoc.Descendants("person")
    select new Player
    {
        FirstName = p.Element("fname").AsString(),
        LastName = p.Element("lname").AsString()
        Leap1 = p.Descendants("try1").FirstOrDefault().AsInt(),
        Leap2 = p.Descendants("try2").FirstOrDefault().AsInt(),
        Leap3 = p.Descendants("try3").FirstOrDefault().AsInt()
    };

如果'player'节点的后代中没有'try1'节点,该FirstOrDefault()方法将返回null。然后AsInt()使用空引用调用扩展方法this,它会检测并返回该空引用,default(int)而不是抛出异常。

您也可以编写一堆ConvertElementToXXX(elem, defValue)方法,但我认为这是对扩展的合理有效使用。XElement没有实现只是一种耻辱IConvertible

于 2013-02-15T00:52:37.017 回答
0

我知道我对这个问题太过分了,但是在摆弄它之后,我决定用 XmlSerialization 重写你的示例。根据您的需要,我强烈建议您将数据序列化为对象,然后传递它们。

您将需要 usingusing System.Xml.Serialization;命名空间,但如果您想使用其中任何一个,我编写了以下完整程序。

public class Program
{
    [XmlRoot("root")]
    public class Team
    {
        private List<Player> players = new List<Player>();

        [XmlElement("player")]
        public List<Player> Players { get { return this.players; } set { this.players = value; } }

        // serializer requires a parameterless constructor class
        public Team() { }
    }

    public class Player
    {
        private List<int> verticalLeaps = new List<int>();

        [XmlElement]
        public string FirstName { get; set; }
        [XmlElement]
        public string LastName { get; set; }
        [XmlElement]
        public List<int> vertLeap { get { return this.verticalLeaps; } set { this.verticalLeaps = value; } }

        // serializer requires a parameterless constructor class
        public Player() { }
    }

    static public void Main(string[] args)
    {
        Program myProgram = new Program();
        myProgram.WritePlayertoDatabase();
        myProgram.ReadPlayerDatabase();
    }

    public void WritePlayertoDatabase()
    {
        Player p1 = new Player() { FirstName = "dwight", LastName = "howard", vertLeap = new List<int>() { 1, 2, 3 } };
        Player p2 = new Player() { FirstName = "dwight", LastName = "howard", vertLeap = new List<int>() { 1 } };

        Team players = new Team();
        players.Players.Add(p1);
        players.Players.Add(p2);
        XmlSerializer serializer = new XmlSerializer(typeof(Team));
        using (TextWriter textWriter = new StreamWriter(@"C:\temp\temp.txt"))
        {
            serializer.Serialize(textWriter, players);
            textWriter.Close();
        }
    }

    public void ReadPlayerDatabase()
    {
        Team myTeamData = new Team();
        XmlSerializer deserializer = new XmlSerializer(typeof(Team));
        using (TextReader textReader = new StreamReader(@"C:\temp\temp.txt"))
        {
            myTeamData = (Team)deserializer.Deserialize(textReader);
            textReader.Close();
        }
    }
}
于 2013-02-15T01:18:22.587 回答