Thursday, March 31, 2011

How would I use LINQ2XML in this scenario?

I have my LINQ2XML query working half way to my goal:

var XMLDoc = XDocument.Load("WeatherData.xml");

var maximums = from tempvalue in 
                   XMLDoc.Descendants("temperature").Elements("value")
               where tempvalue.Parent.Attribute("type").Value == "maximum"
               select (string)tempvalue;

var minimums = from tempvalue in 
                   XMLDoc.Descendants("temperature").Elements("value")
               where tempvalue.Parent.Attribute("type").Value == "minimum"
               select (string)tempvalue;

List<string> MaxTemps = maximums.ToList();
List<string> MinTemps = minimums.ToList();

However, I'm having trouble with getting the time information from the XML document, because I have to match the layout-key information(see the XML comments), and I'm wondering what would be the best solution in LINQ to join this time data with my existing queries:

(By the way, this XML data comes from a web service)

<?xml version="1.0" encoding="utf-8"?>
<dwml>
  <data>
    <time-layout>
      <!--        Maximums Key         -->
      <layout-key>k-p24h-n7-1</layout-key>
      <start-valid-time>2009-02-09T07:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-09T19:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-10T07:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-10T19:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-11T07:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-11T19:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-12T07:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-12T19:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-13T07:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-13T19:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-14T07:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-14T19:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-15T07:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-15T19:00:00-05:00</end-valid-time>
    </time-layout>
    <time-layout>
      <!--        Minimums Key         -->
      <layout-key>k-p24h-n7-2</layout-key>
      <start-valid-time>2009-02-08T19:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-09T08:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-09T19:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-10T08:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-10T19:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-11T08:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-11T19:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-12T08:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-12T19:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-13T08:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-13T19:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-14T08:00:00-05:00</end-valid-time>
      <start-valid-time>2009-02-14T19:00:00-05:00</start-valid-time>
      <end-valid-time>2009-02-15T08:00:00-05:00</end-valid-time>
    </time-layout>
    <parameters>
      <!--                                     1st Key   -->
      <temperature type="maximum" time-layout="k-p24h-n7-1">
        <value>44</value>
        <value>57</value>
        <value>55</value>
        <value>40</value>
        <value>39</value>
        <value>34</value>
        <value>33</value>
      </temperature>
      <!--                                     2nd Key   -->
      <temperature type="minimum" time-layout="k-p24h-n7-2">
        <value>24</value>
        <value>38</value>
        <value>46</value>
        <value>35</value>
        <value>25</value>
        <value>27</value>
        <value>23</value>
      </temperature>
    </parameters>
  </data>
</dwml>
From stackoverflow
  • I would start out by breaking this down into smaller pieces. First, I would massage the time layouts into a more workable form, grouped by the layout key, with the valid start time and valid end time associated with each other:

    var timeLayouts =
        from tempvalue in XMLDoc.Descendants("time-layout")
        let tempStartTimes = tempvalue.Elements("start-valid-time").
                Select((x, i) => new { Index = i, ValidDateTime = (DateTime)x })
        let tempEndTimes = tempvalue.Elements("end-valid-time").
                Select((x, i) => new { Index = i, ValidDateTime = (DateTime)x })
        select new
        {
            LayoutKey = tempvalue.Element("layout-key").Value,
            ValidTimeRanges =
                from s in tempStartTimes
                from e in tempEndTimes
                where s.Index == e.Index
                select new 
                { 
                    Index = s.Index, 
                    ValidStartDateTime = s.ValidDateTime, 
                    ValidEndDateTime = e.ValidDateTime 
                }
        };
    

    Then, I would massage the parameters in much the same way:

    var parameters =
        from tempvalue in XMLDoc.Descendants("temperature")
        select new
        {
            TemperatureType = (string) tempvalue.Attribute("type"),
            TimeLayout = (string) tempvalue.Attribute("time-layout"),
            Temperatures = tempvalue.Elements("value").Select((x, i) =>
                new { Index = i, Temperature = (int)x })
        };
    

    From there, it's not so hard to get your maximums and minimums:

    var maximums =
        from p in parameters
        where p.TemperatureType == "maximum"
        from tl in timeLayouts
        where tl.LayoutKey == p.TimeLayout
        from tr in tl.ValidTimeRanges
        from t in p.Temperatures
        where tr.Index == t.Index
        select new { tr.ValidStartDateTime, tr.ValidEndDateTime, 
            t.Temperature };
    
    var minimums =
        from p in parameters
        where p.TemperatureType == "minimum"
        from tl in timeLayouts
        where tl.LayoutKey == p.TimeLayout
        from tr in tl.ValidTimeRanges
        from t in p.Temperatures
        where tr.Index == t.Index
        select new { tr.ValidStartDateTime, tr.ValidEndDateTime, 
            t.Temperature };
    

    You could go some other ways with this, if you wanted to simplify some of the representations (you could flatten out the layouts and parameters into something more "tabular", for example), it would just require a few tweaks.

    M4dRefluX : Thank you so much! I've been pulling my hair out trying to figure this out, reading books, code examples, etc. and nothing was suitable to my scenario. Your timeLayouts query will serve me well for pretty much the entire SOAP service.
    casperOne : @M4dRefluX: No problem. If you have any questions about the specifics, feel free to ask.

0 comments:

Post a Comment