Search code examples
c#xmllinq-to-xmllinq-to-xsd

How to save and get values from an Xml without cast?


Something which I really hate is to cast each element or attribute value from a Xml file.

This momment, I'm creating in hundreds of modules a methods where specifies how to convert an object into a XmlFile. Believe, this is very tired. So I'm thinking in an alternative.

I was investigating about XSD, I'm not sure if this will be my salvation. I'm using Linq to Xml to save and get the values. I mean, my objects are composed like this:

- Foo1 : Foo
   - Range1 : Range
      - X : int
      - Y : int
- ...

As you can see, they have many nodes. Is there another alternative to do this? I mean, strongly types.


Solution

  • You can try these XElement extension methods: http://searisen.com/xmllib/extensions.wiki

    Here is an example of the power of it, given this xml from another post:

    <?xml version="1.0" encoding="utf-8"?>
    <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
      <PatchCreation
        Id="224C316C-5894-4771-BABF-21A3AC1F75FF"
        CleanWorkingFolder="yes"
        OutputPath="patch.pcp"
        WholeFilesOnly="yes">
        <PatchInformation
            Description="Update Patch"
            Comments="Update Patch"
            ShortNames="no"
            Languages="1033"
            Compressed="yes"
            Manufacturer="me"/>
    
        <PatchMetadata
            AllowRemoval="yes"
            Description="Update Patch"
            ManufacturerName="me"
            TargetProductName="Update"
            MoreInfoURL="http://andrewherrick.com/"
            Classification="Update"
            DisplayName="Update Patch"/>
    
        <Family DiskId="5000"
            MediaSrcProp="Sample"
            Name="Update"
            SequenceStart="5000">
          <UpgradeImage SourceFile="c:\new.msi" Id="PatchUpgrade">
            <TargetImage SourceFile="c:\old.msi" Order="2" Id="PatchUpgrade" IgnoreMissingFiles="no" />
          </UpgradeImage>
        </Family>
    
        <PatchSequence PatchFamily="SamplePatchFamily"
            Sequence="1.0.0.0"
            Supersede="yes" />
      </PatchCreation>
    </Wix>
    

    This sets the value of the UpgradeImage tag's SourceFile Attribute and the TargetImage tag inside the UpgradeImage and its SourceFile.

    XElement wix = XElement.Load(xmlFile1.FullName);
    wix.Set("PatchCreation/Family/UpgradeImage/SourceFile", "upgrade path", true)
       .Set("TargetImage/SourceFile", "target path", true);
    

    You can also get their values in the same fashion (no casts).

    string upgradeSource = wix.Get("PatchCreation/Family/UpgradeImage/SourceFile", string.Empty);
    string targetSource = wix.Get("PatchCreation/Family/UpgradeImage/TargetImage/SourceFile", string.Empty);
    

    Or this can be written as:

    XElement upgradeImage = wix.GetElement("PatchCreation/Family/UpgradeImage");
    string upgradeSource = upgradeImage.Get("SourceFile", string.Empty);
    string targetSource = upgradeImage.Get("TargetImage/SourceFile", string.Empty);
    

    To get a list of integers:

    <root>
     <path>
      <list>
        <value>1</value>
        <value>12</value>
        <value>13</value>
        <value>14</value>
        <value>15</value>
       </list>
      </path>
    </root>
    

    Use the GetEnumerable() method:

    List<int> list = root
        .GetEnumerable("path/list/value", xvalue => xvalue.Get(null, int.MinValue));
        .ToList();
    

    To set a new list of ints:

    var list2 = new int[] { 1, 3, 4, 5, 6, 7, 8, 9, 0 };
    root.SetEnumerable("path/list", list2, a => new XElement("value", a));
    

    Which results in this new xml:

    <root>
      <path>
        <list>
          <value>1</value>
          <value>3</value>
          <value>4</value>
          <value>5</value>
          <value>6</value>
          <value>7</value>
          <value>8</value>
          <value>9</value>
          <value>0</value>
        </list>
      </path>
    </root>