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ではありませんが、この例では機能するはずです。次に、linqtoxmlを使用してデータを読み取ります。Iamはこのようにしようとしています。

クラス:

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")文字列ではなく、fnameXML要素を返します。したがって、要素の値を取得する必要があります。同様に、Leap1-3プロパティはintですが、要素値を攪拌として取得し、それを変換する必要があります。ただし、try1-3はxmlのintではないため、Playerクラスでタイプを他の何かに変更することをお勧めします。

2)try1-tryx要素はすべて「vertleap」の子です。'person'から要素'try1'を直接取得することはできません。nullになります。

では、次のようなものはどうでしょうか。

    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>")@SimonCが指摘したように、呼び出しは常にnulllを返します... 値を取得するには、XMLツリーをトンネリングするか、を使用Descendants("try<x>").FirstOrDefault()して、名前が一致する最初の子孫を取得する必要があります。これはまだnullである可能性があり、質問の本当のポイントに戻ります。

問題は、実際には、存在しない可能性のあるオブジェクトに対して操作を実行しようとしていることです。@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()拡張メソッドがnull参照を使用して呼び出され、例外をスローする代わりに、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 に答える