LINQ: Load XML into a Dictionary with value equaling new custom class instances

Struggling a bit with two LINQ statements here. Basically I would like to convert the following two XML files into dictionaries (type-detail to follow). Here are snapshots of the XMLs:

Offset XML:

<Offsets>
  <PlayerStructBase>0xF24C10</PlayerStructBase>
  <HP>
    <offset>0x17e8</offset>
  </HP>
  <MaxHP>
    <offset>0x17ec</offset>
  </MaxHP>
</Offsets>

Desired output: Dictionary<string, IntPtr>. I have a method called GetPointerFromBaseOffsets(int[] offsets) that returns the IntPtr from a int array with offsets (example: 0x1234, 0x17e2).

Skills XML:

<Skills>
  <Potion>
    <Cast>0.00</Cast>
    <ReCast>60.00</ReCast>
    <MPCost>0</MPCost>
  </Potion>
  <Ruin>
    <Cast>2.49</Cast>
    <ReCast>2.49</ReCast>
    <MPCost>9</MPCost>
  </Ruin>
</Skills>

Desired output: Dictionary<string, Skill>. Skill is a class with properties Cast, ReCast and MpCost among other things.


These are my attempts:

Offset XML to Dictionary

OffsetDictionary =
    XDocument.Load(folderPath+@"\offsets.xml")
        .XPathSelectElements("/Offsets/*[offset]")
        .ToDictionary(o => o.Name.LocalName,
            o => MemoryManager.GetPointerFromBaseOffsets(Enumerable.Cast<int>(o.Elements()).ToArray()));

Skills XML to Dictionary

SkillDictionary =
    XDocument.Load(folderPath + @"\skills.xml")
        .XPathSelectElements("/Skills/*")
        .ToDictionary(e => e.Name.LocalName, e => new Skill(e.Name.LocalName, (double)e.Element("Cast"), (double)e.Element("ReCast"), (int)e.Element("MPCost")));

Question: On the attempt at creating the Offset dictionary I get my first error-message (run-time), that it cannot be Cast. Could someone show me how to write these two blocks?

Thanks!

Jon Skeet
people
quotationmark

Enumerable.Cast doesn't perform custom conversions, which is what you want. You need to cast directly - but that's pretty simple:

OffsetDictionary =
    XDocument.Load(folderPath+@"\offsets.xml")
        .XPathSelectElements("/Offsets/*[offset]")
        .ToDictionary(o => o.Name.LocalName,
            o => MemoryManager.GetPointerFromBaseOffsets(o.Elements()
                                                          .Select(x => (int) x)
                                                          .ToArray()));

However, that assumes that your offsets are actually just plain decimal integers. In your case, they're not - they're in hex. You'll need to do a bit more work to parse those, e.g.

.Select(x => int.Parse(x.Value.Substring(2), NumberStyles.AllowHexSpecifier))

Alternatively, change your XML format so that the values are in decimal.

I'm not sure about your XPath expression though... I'm not an XPath expert by any means, but don't you just want all the elements directly beneath the root element? If so, you can just use

.Root.Elements()

instead of your XPathSelectElements call.

people

See more on this question at Stackoverflow