0

Given the following requirements and code, I have not yet found ONE answer that actually works.

I have an XML field in a SQL Server Database table. Why is it in there? I have no idea. I didn't put it in there. I just have to get the data out and into a List that I can combine with another List to populate a grid in a WPF app that has an MVVM architecture.
Here is that List: List QAItems = new List();

The QADailyXValueCalCheck type is as follows:

     using System.Xml.Serialization;

    namespace ConvertXmlToList {
    [XmlRoot(ElementName = "column")]
public class QADailyXValueCalCheck {
[XmlElement] 
public string Regs { get; set; }
    [XmlElement]
    public string BasisTStamp { get; set; }
    [XmlElement]
    public string DAsWriteTStamp { get; set; }
    [XmlElement]
    public string InjEndTime { get; set; }
    [XmlElement]
    public bool Manual { get; set; }
    [XmlElement]
    public decimal RefValue { get; set; }
    [XmlElement]
    public decimal MeasValue { get; set; }
    [XmlElement]
    public string Online { get; set; }
    [XmlElement]
    public decimal AllowableDrift { get; set; }
[XmlElement]
public bool FailOoc { get; set; } = false;
    [XmlElement]
    public bool FailAbove { get; set; }
    [XmlElement]
    public bool FailBelow { get; set; }
    [XmlElement]
    public bool FailOoc5Day { get; set; }
    [XmlElement]
    public decimal InstSpan { get; set; }
    [XmlElement]
    public decimal GasLevel { get; set; }
    [XmlElement]
    public string CId { get; set; }
    [XmlElement]
    public string MId { get; set; }
    [XmlElement]
    public string CylinderId { get; set; }
    [XmlElement]
    public string CylinderExpDate { get; set; }
    [XmlElement]
    public string CylinderVendorId { get; set; }
    [XmlElement]
    public string CylinderGasTypeCode { get; set; }
        }
    }

The XML is being stored in a string, xmlString and comes out of the db in the following form:

    <Regs>40CFR75</Regs>
    <BasisTStamp>2016-02-15 05:18</BasisTStamp>
    <DASWriteTStamp>2016-02-15 05:40</DASWriteTStamp>
    <InjEndTime>2016-02-15 05:23</InjEndTime>
    <Manual>0</Manual>
    <RefValue>169.7</RefValue>
    <MeasValue>169.27</MeasValue>
    <Online>14</Online>
    <AllowableDrift>15</AllowableDrift>
    <FailAbove>0</FailAbove>
    <FailBelow>0</FailBelow>
    <InstSpan>300</InstSpan>
    <GasLevel>MID</GasLevel>
    <CID>111</CID>
    <MID>N10</MID>
    <CylinderID>CC357464</CylinderID>
    <CylinderExpDate>2022-08-12</CylinderExpDate>
    <CylinderVendorID>B22014</CylinderVendorID>
    <CylinderGasTypeCode>BALN,SO2,NO,CO2</CylinderGasTypeCode>

Now, in order to get past the XML API's problem with "rootless" xml, I've added a root:

    xmlString = "<columns>" + xmlString + "</columns>";

To parse this, I use:

    XDocument doc = XDocument.Parse(xmlString);

Finally, to attempt to extract the VALUES from the XML and populate an instance of QADailyXValueCalCheck, I have the following code - which was adapted to work from other examples - THAT DO NOT WORK.

                var xfields =
            from r in doc.Elements("columns")
            select new QADailyXValueCalCheck
            {
                Regs = (string) r.Element("Regs"),
                BasisTStamp = (string) r.Element("BasisTStamp"),
                DAsWriteTStamp = (string) r.Element("DASWriteTStamp"),
                InjEndTime = (string) r.Element("InjEndTime"),
                Manual = (bool) r.Element("Manual"),
                RefValue = (decimal) r.Element("RefValue"),
                MeasValue = (decimal)r.Element("MeasValue"),
                Online = (string)r.Element("Online"),
                AllowableDrift = (decimal)r.Element("AllowableDrift"),
                //FailOoc = (bool)r.Element("FailOoc"),
                //FailAbove = (bool)r.Element("FailAbove"),
                //FailBelow = (bool)r.Element("FailBelow"),
                //FailOoc5Day = (bool)r.Element("FailOoc5Day"),
                //InstSpan = (decimal)r.Element("InstSpan"),
                //GasLevel = (decimal)r.Element("GasLevel"),
                CId = (string)r.Element("CID"),
                MId = (string)r.Element("MID"),
                CylinderId = (string)r.Element("CylinderId"),
                CylinderExpDate = (string)r.Element("CylinderExpDate"),
                CylinderVendorId = (string)r.Element("CylinderVendorId"),
                CylinderGasTypeCode = (string)r.Element("CylinderGasTypeCode")
            };

The code immediately above does NOT create a new instance of the class, "QADailyXValueCalCheck" which can be added to the List. I have a few null values that are causing a problem with that code, but that is a separate issue that I will deal with another time.

For now, can anyone tell me how that "var xfields = " query instantiates a new QADailyXValueCalCheck object that can be added to my List of the same type?

What code is missing? Thank you to the LINQ/XML genius that can answer this.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445

1 Answers1

0

I had a similar question on XML parsing the other day from someone else here: Create a List from XElements Dynamically

I think in the end you would be better served using a class with xml adornments and then have extension classes that serialize or deserialize the data. This makes it better IMHO in two ways: 1. You don't have to rewrite the parser and the POCO class, just the POCO class. 2. You can be free to reuse the extension method in other places.

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace GenericTesting.Models
{
  [XmlRoot(ElementName = "column")]
  public class QADailyXValueCalCheck
  {
  [XmlElement]
  public string Regs { get; set; }
  [XmlElement]
  public string BasisTStamp { get; set; }
  [XmlElement]
  public string DAsWriteTStamp { get; set; }
  [XmlElement]
  public string InjEndTime { get; set; }
  [XmlElement]
  public int Manual { get; set; }
  [XmlElement]
  public decimal RefValue { get; set; }
  [XmlElement]
  public decimal MeasValue { get; set; }
  [XmlElement]
  public int Online { get; set; }
  [XmlElement]
  public decimal AllowableDrift { get; set; }
  [XmlElement]
  public bool FailOoc { get; set; } = false;
  [XmlElement]
  public int FailAbove { get; set; }
  [XmlElement]
  public int FailBelow { get; set; }
  [XmlElement]
  public bool FailOoc5Day { get; set; }
  [XmlElement]
  public decimal InstSpan { get; set; }
  [XmlElement]
  public string GasLevel { get; set; }
  [XmlElement]
  public string CId { get; set; }
  [XmlElement]
  public string MId { get; set; }
  [XmlElement]
  public string CylinderId { get; set; }
  [XmlElement]
  public string CylinderExpDate { get; set; }
  [XmlElement]
  public string CylinderVendorId { get; set; }
  [XmlElement]
  public string CylinderGasTypeCode { get; set; }
  }
}

And for the purpose of serializing/deserializing let me give extension methods for those:

using System.IO;        
using System.Xml;
using System.Xml.Serialization;

namespace GenericTesting
{                                   
  static class ExtensionHelper
  { 
    public static string SerializeToXml<T>(this T valueToSerialize)
    {
      dynamic ns = new XmlSerializerNamespaces();
      ns.Add("", "");
      StringWriter sw = new StringWriter();

      using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true }))
      {
        dynamic xmler = new XmlSerializer(valueToSerialize.GetType());
        xmler.Serialize(writer, valueToSerialize, ns);
      }

      return sw.ToString();
    }

    public static T DeserializeXml<T>(this string xmlToDeserialize)
    {
      dynamic serializer = new XmlSerializer(typeof(T));

      using (TextReader reader = new StringReader(xmlToDeserialize))
      {
        return (T)serializer.Deserialize(reader);
      }
    }
  }
}

And a simple main entry point in a console app:

static void Main(string[] args)
{
    var thingie = new QADailyXValueCalCheck
    {
      Regs = "40CFR75",
      BasisTStamp = "2016-02-15 05:18",
      DAsWriteTStamp = "2016-02-15 05:40",
      InjEndTime = "2016-02-15 05:23",
      Manual = 0,       //Boolean This will probably mess up  Changed to Int
      RefValue = 169.7M,
      MeasValue = 169.27M,
      Online = 14,  //Mismatch Type?  Change to Int
      AllowableDrift = 15,
      FailAbove = 0,     //Boolean This will probably mess up   Changed to Int
      FailBelow = 0,     //Boolean This will probably mess up    Changed to Int
      InstSpan = 300,
      GasLevel = "MID",     //This is marked as a decimal?     Changed to string                                                
      CId = "111",             
      MId =  "N10",
      CylinderId= "CC357464",
      CylinderExpDate ="2022-08-12",
      CylinderVendorId = "B22014",
      CylinderGasTypeCode = "BALN,SO2,NO,CO2"
  };

  var serialized = thingie.SerializeToXml();

  var deserialized = serialized.DeserializeXml<QADailyXValueCalCheck>();

  Console.ReadLine();
}

This serializes just like this and I can get it back to deserialized as well.

example serialization

Community
  • 1
  • 1
djangojazz
  • 14,131
  • 10
  • 56
  • 94
  • Thank you for sharing. I'll check this out. – VedicSWEngineer Feb 02 '17 at 20:12
  • No prob, you have the bulk of the work done already by having the POCO class already written with the adornments. XDocument works great at times when you have a structure of XML you want to get parts of it and then transfer into something else. Say XML structure from a 3rd party and you only want some of it. But if you are getting everything xml to everything well formed, adorning classes properly and doing a deserialization method is much simpler and extensible for later reuse. – djangojazz Feb 02 '17 at 20:16
  • I might point out that the list "locations", starts with three elements, and never increases to 4. Nor is there any actual xml that is then converted into a NEW location, to increase the count to 4. Interesting code, but.... – VedicSWEngineer Feb 02 '17 at 20:35
  • Ah, eureka! Found some deserialization going on. I should've stepped through more carefully. – VedicSWEngineer Feb 02 '17 at 20:48
  • I just use the comment ',' at the end because I am lazy when I copy and paste and C# allows it so ... ;). The beauty in the code is the repeatable use of it. I love me some encapsulation. – djangojazz Feb 02 '17 at 21:20
  • Unfortunately, the line "return (T) serializer.Deserialize(reader);" goes tango uniform on my XML. I get an " was not expected." message in the "public static T DeserializeXml(this string xmlToDeserialize)" method of the ExtensionHelper class. – VedicSWEngineer Feb 02 '17 at 21:31
  • I recommend that any answers provided - USE the xml that comes to MY "table". That's what I have to work with. It isn't well-formed and the XML API does NOT like it - which is why I have to add the root element of "" – VedicSWEngineer Feb 02 '17 at 21:32
  • You are not showing the complete xml though ;). You are only showing elements nodes asfdefetc.. That error is essentially saying: "I don't know this namespace" Just remove the namespace and it should work. Hold up I will update the answer. – djangojazz Feb 02 '17 at 22:13
  • What you see is ALL I HAVE. THAT is what's stored in the XValue field of the table. There ISN'T any "complete XML". – VedicSWEngineer Feb 02 '17 at 22:18
  • Your types are not even matching your XML lol. No wonder. You have Decimal for MID value of Type GasLevel in your object? Are you like Jeff Goldblum and you work with the number "a"? – djangojazz Feb 02 '17 at 22:25
  • Updated code, I don't know what it could be blowing up on, it could be potentially how you are building the xml in .NET. Generally SQL Server will allow just element nodes without ROOTs, .NET when serializing will want a root node. – djangojazz Feb 02 '17 at 22:41
  • Would anyone agree that what I have isn't truly "XML" since it is not well-formed and can be termed "a bunch of elements surrounded with xml-like tags" which someone was trying to pass off - as xml? I believe that's what I'll call it. Most of the XML API has "issues" with this so-called "xml", but that's what I have to deal with. A more primitive solution may be in order, I think. – VedicSWEngineer Feb 02 '17 at 23:15
  • Well SQL Server will accept just elements and I forgot that until I checked this declare @Xml xml = 'abcdef' and it works just fine. The issue is going to be 'how' you get that xml from the sql server. If you are able to easily wrap it up in the root '' it should be fairly easy to get info out of it well formed. – djangojazz Feb 02 '17 at 23:20
  • Very good, djangojazz. Much appreciation for your instruction. Thank you. – VedicSWEngineer Feb 03 '17 at 16:20
  • No prob, if you are happy with the answer you can mark it complete if you like or else others may come give you other answers as well later. – djangojazz Feb 06 '17 at 16:23