2012-10-02 24 views
7

Próbuję zapobiec wartości NULL podczas parsowania pliku XML do niestandardowego obiektu przy użyciu LINQ.C# ?? operator koalesulacji zerowej LINQ

Znalazłem świetne rozwiązanie dla tego na Scott Gu's blog, ale z jakiegoś powodu nie działa dla liczb całkowitych ze mną. Myślę, że użyłem tej samej składni, ale wydaje mi się, że czegoś brakuje. Aha i z jakiegoś powodu działa, gdy węzeł nie jest pusty.

Poniżej znajduje się wyciąg z mojego kodu.

List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname") 
     select new GrantAgresso() 
     { 
      Year = (int?)g.Element("yearnode") ?? 0, 
      Subdomain = (string)g.Element("domainnode") ?? "" 
     }).ToList(); 

ErrorMessage jest:

Input string was not in a correct format.

Jeśli ktoś ma pojęcia, co robię źle, proszę o pomoc :)

Edycja: kawałek XML (dziwne nazwy, ale to nie z wyboru)

<Agresso> 
    <AgressoQE> 
    <r3dim_value>2012</r3dim_value> 
    <r0r0r0dim_value>L5</r0r0r0dim_value> 
    <r7_x0023_province_x0023_69_x0023_V005>0</r7_x0023_province_x0023_69_x0023_V005> 
    <r7_x0023_postal_code_x0023_68_x0023_V004 /> 
    <r7_x0023_country_x0023_67_x0023_V003>1004</r7_x0023_country_x0023_67_x0023_V003> 
    <r7_x0023_communitydistrict_x0023_70_x0023_V006>0</r7_x0023_communitydistrict_x0023_70_x0023_V006> 
    </AgressoQE> 
</Agresso> 
+1

Mój zakład jest na niewłaściwej składni XML. Czy możesz pokazać zawartość xml? Operator koalescji ma rację, a jeśli nie, to nie wyrzuci tego błędu (nie ma nic wspólnego z łańcuchami). –

+0

Czy przypisanie "Rok" lub "Subdomena" powoduje wyjątek? Możesz wypróbować komentarz w jednym z wierszy, aby sprawdzić, czy wyjątek zniknie. – Mizipzor

+0

Dodałem węzeł xml do mojego posta. Jest to pusty węzeł (zaczynający się od r7_), który uruchamia wyjątek. Kod, którego użyłem do jego pobrania, jest taki sam jak ten, z którego korzystałem przez rok. – Nielsm

Odpowiedz

1

Poniższa metoda rozszerzenie powróci 0 zarówno jeśli element nie jest obecny, element jest pusta lub zawiera ciąg, który nie może być analizowany na integer:

public static int ToInt(this XElement x, string name) 
    { 
     int value; 
     XElement e = x.Element(name); 
     if (e == null) 
      return 0; 
     else if (int.TryParse(e.Value, out value)) 
      return value; 
     else return 0; 
    } 

Można używać go tak:

... 
Year = g.ToInt("r3dim_value"), 
... 

lub jeśli jesteś gotowy, aby wziąć pod uwagę koszty i refleksji, aby zaakceptować wartość domyślną wartość dowolnego typu, można użyć tej metody rozszerzenia:

public static T Cast<T>(this XElement x, string name) where T : struct 
{ 
    XElement e = x.Element(name); 
    if (e == null) 
     return default(T); 
    else 
    { 
     Type t = typeof(T); 
     MethodInfo mi = t.GetMethod("TryParse", 
            BindingFlags.Public | BindingFlags.Static, 
            Type.DefaultBinder, 
            new Type[] { typeof(string), 
               t.MakeByRefType() }, 
            null); 
     var paramList = new object[] { e.Value, null }; 
     mi.Invoke(null, paramList); 
     return (T)paramList[1]; //returns default(T), if couldn't parse 
    } 
} 

i używać go:

... 
Year = g.Cast<int>("r3dim_value"), 
... 
+0

Użyłem twojej drugiej sugestii. Dzięki! – Nielsm

1

Możesz dodać Where operator

..... 
.Where(a => ! string.IsNullOrEmpty(a)).ToList(); 
+0

Co to jest "a"? Dlaczego nazywasz 'ToList' na czymś, co wygląda na' IEnumerable '? – Rawling

+2

Nie robi to, co OP robi. –

+1

Nie, nie mogę, nie chcę filtrować wyników, po prostu nie chcę, aby mój program się zepsuł, gdy pojawi się pusty węzeł ... – Nielsm

1

Jeśli rok jest zerowy lub pusty ciąg, który dostaniesz „ciąg wejściowy nie był w odpowiednim formacie” wyjątku. Możesz napisać metodę rozszerzenia, aby odczytać wartości. Nie testowałem kodu poniżej, ale może dać ci kilka wskazówek.

public static ReadAs<T>(this XElement el, T defaultValue) { 
    var v = (string)el; // cast string to see if it is empty 

    if (string.IsNullOrEmpty(v)) // test 
    return defaultValue; 

    return (T)el; // recast to our type. 
} 
2

Jest to wyrażenie, które rzuca:

(int?)g.Element("yearnode") 

To dlatego, jeśli rzeczywista wartość węzła tekstowego elementu jest String.Empty i nie null, gdyż pusty ciąg nie jest poprawnym formatem Int32.Parse , próba rzucenia nie udała się.

Jeśli brakuje tego elementu w pliku XML, działa to zgodnie z oczekiwaniami, ale jeśli jest pusty tag <yearnode/> lub <yearnode></yearnode>, otrzymasz wyjątek.

+0

To może być odpowiedź, której szukałem. Co byś zasugerował? – Nielsm

1

Komunikat Input string was not in a correct format wygląda jak jeden rzucony przez int.parse() więc może się okazać, że masz yearnode o wartości (not null), ale nie może być przetworzony na wartość całkowitą.

Coś jak to może go naprawić:

List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname") 
    let yearNode = g.Element("yearnode") 
    select new GrantAgresso 
    { 
     Year = string.IsNullOrWhiteSpace(yearNode.Value) ? 0 : int.Parse(yearNode.Value), 
     Subdomain = g.Element("domainnode").Value 
    }).ToList(); 

Kilka rzeczy do uwaga:

select new GrantAgresso - nie trzeba nawias dla domyślnego konstruktora z inicjatorów obiektu.

string.IsNullOrWhiteSpace - został wprowadzony w .net 4.0, użyj string.IsNullOrEmpty, jeśli masz 3.5 lub wcześniej

g.Element("domainnode").Value - zawsze zwraca ciąg

jeśli chcesz null roku zamiast 0, użyj (int?)null zamiast 0

+0

Witam, Mam właśnie podwójne i potrójne sprawdzenie mojego XML, wartości, które chcę przekonwertować na int są puste lub poprawną liczbą całkowitą. Jest to coś, co również rozważałem ;-) – Nielsm

+0

Zaktualizowałem swoją odpowiedź z sugestią dotyczącą usuwania opróżnień. –

0

Twój problem jest, że obsada z Xelement do int? używa metody int.Parse na wartości ciągu XElement, która w twoim przypadku to String.Empty. Poniższe wyniki w ten sam błąd:

XElement x = new XElement("yearnode", String.Empty); 
int? a = (int?)x; // throws FormatException 

Można tego uniknąć przez pierwszy odlewania Xelement ciąg i sprawdzanie, czy jest null lub pusty i tylko wtedy, gdy nie robi obsadę do int ?. Aby to zrobić, należy wymienić

Year = (int?)g.Element("yearnode") ?? 0, 

z

Year = !string.IsNullOrEmpty((string)g.Element("yearnode")) ? (int?)g.Element("yearnode") : 0, 

Jej nie całkiem i będzie nadal rzucać jeśli ciąg jest inaczej nie legalne, ale to działa, czy można przyjąć, że element jest zawsze liczbą całkowitą lub null/empty.