16

I have an XML schema .xsd file and generate my file with all the C# classes with the xsd.exe tool. If I have a sequence of elements within an XML tag, that would be represented in C# with an array. The fail is obvious. How can I generate Lists instead of arrays?

Instead of fixed size arrays in a class, I would like to use Lists.

Book [] books = new Book[someFixSize];

List<Book> books =  new List<Book>();

I have seen some older (very old) questions about this, but none of them provided a satisfied solution :/

This is the latest useful hint: http://www.stefanbader.ch/xsdcsarr2l-exe-refactor-xsd-array-to-list/

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gero
  • 12,993
  • 25
  • 65
  • 106

5 Answers5

4

I run into the same problem trying to use the svcutil without having the contracts, for that reason I wrote the xsdcsarr2l tool. If you are interested I take the time and upload a newer version where at least the list variables are initialized automatically. On the other hand, the project is light enough, that you can take the source and improve it yourself by using the NRefactory classes.

EvilBad
  • 80
  • 2
  • Are you simple replacing the line that contains an Array [] with a List? Or are any other lines of the code edited too? – Gero Mar 15 '13 at 21:47
  • Just the lines where the fixed arrays are involved. The tool is parsing the c#-source and transferring it into an AST (Abstract Syntax Tree) with the help of NRefactory. So the changes are applied in a robust way. The idea was to touch as less as possible of the original. – EvilBad Mar 18 '13 at 20:12
  • yeah, upload the latest version. – Gero Mar 19 '13 at 12:42
  • OK will do, give me a day to complete that. Will drop a note here when the download is ready. – EvilBad Mar 20 '13 at 00:02
  • 1
    Done. Latest version [0.0.2](http://www.stefanbader.ch/xsdcsarr2l-exe-version-0-0-2-out) uploaded. Send a note if you encounter any problems. – EvilBad Mar 20 '13 at 04:23
  • Awesome. Thanks a lot @EvilBad . I used your tool and was able to successfully convert the arrays into List. – HelpMatters Jul 03 '14 at 14:52
  • 2
    is that tool available anymore? the links are broken above – George Birbilis Nov 11 '20 at 10:16
2

Try using svcutil.exe

svcutil /o:myFile.cs /ct:System.Collections.Generic.List myXsd.xsd
CathalMF
  • 9,705
  • 6
  • 70
  • 106
2

Dan Field has a PowerShell script that takes an xsd.exe output class and turns its arrays into generic lists. This has worked well for me with a simple class, but I don't know how well it scales. I've pasted the script below. Call it from a command prompt like this:

"$(TargetFrameworkSDKToolsDirectory)xsd.exe" /c "$(ProjectDir)ImportedPartCanonical.xsd" "$(ProjectDir)ProjectCanonical.xsd" /n:Tallan.BT.PipelineComponents

powershell.exe -ExecutionPolicy Unrestricted -file "$(solutiondir)\PowerShellScripts\PostProcessXsdExe.ps1" ProjectCanonical.cs "$(SolutionDir)Tallan.BT.PipelineComponents\SerializedClasses\ProjectCanonical.cs"

See the link for the full explanation.

# Author: Dan Field (dan.field@tallan.com)
# posted on blog.tallan.com/2016/03/10/xsd-exe-arrays-and-specified
# Purpose: fix the 'specified' attribute and convert arrays to list from XSD.exe generated classes

[CmdletBinding()]
Param(
    [Parameter(Mandatory=$true, Position=1)]
    [string]$inputFile,
    [Parameter(Mandatory=$true, Position=2)]
    [string]$outputFile,
    [switch]$DeleteInputFile
)

# Much faster than using Get-Content and/or Out-File/Set-Content
$writer = [System.IO.StreamWriter] $outputFile
$reader = [System.IO.StreamReader] $inputFile

# Used to track Specified properties
$setterDict = @{}

while (($line = $reader.ReadLine()) -ne $null)
{
    $thisStart = $line.IndexOf("this.") # Will be used for
    $brackets = $line.IndexOf("[]") # Indicates an array that will be converted to a Generic List

    # Assume that any private field that contains "Specified" needs to be grabbed
    if (($line.IndexOf("private") -gt -1) -and ($line.IndexOf("Specified") -gt -1))
    {
        # Get the field name
        $varName = $line.Split("{' ', ';'}", [System.StringSplitOptions]::RemoveEmptyEntries)[-1]

        # Use the field name as a key, minus the ending "Specified" portion, e.g. fieldNameSpecified -> fieldName
        # The value in the dictionary will be added to setters on the main property, e.g. "this.fieldNameSpecified = true;"
        $setterDict.Add($varName.Substring(0, $varName.IndexOf("Specified")), "this." + $varName + " = true;")

        # Output the line as is
        $writer.WriteLine($line)
    }
    # Find property setters that aren't for the *Specified properties
    elseif (($thisStart -gt -1) -and ($line.IndexOf(" = value") -gt -1) -and ($line.IndexOf("Specified") -lt 0))
    {
        # Get the field name
        $thisStart += 5
        $varName = $line.Substring($thisStart, $line.IndexOf(' ', $thisStart) - $thisStart)
        # see if there's a "Specified" property for this one
        if ($setterDict.ContainsKey($varName) -eq $true)
        {
            # Set the Specified property whenever this property is set
            $writer.WriteLine((' ' * ($thisStart - 5)) + $setterDict[$varName])
        }
        # Output the line itself
        $writer.WriteLine($line)
    }
    elseif ($brackets -gt 0) # change to List<T>
    {
        $lineParts = $line.Split(' ')
        foreach ($linePart in $lineParts)
        {
            if ($linePart.Contains("[]") -eq $true)
            {
                $writer.Write("System.Collections.Generic.List<" + $linePart.Replace("[]", "> "))
            }
            else
            {
                $writer.Write($linePart + " ")
            }
        }
        $writer.WriteLine();
    }
    else # Just output the original line
    {
        $writer.WriteLine($line)
    }
}

if ($DeleteInputFile -eq $true)
{
    Remove-Item $inputFile
}

# Make sure the file gets fully written and clean up handles
$writer.Flush();
$writer.Dispose();
$reader.Dispose();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrew Olney
  • 691
  • 4
  • 12
0

Try Xsd2Code

It generates lists instead of arrays. Unfortunately I couldn't get it to deserialize my code, but comparing it to the code generated by xsd it looked very similar.

goku_da_master
  • 4,257
  • 1
  • 41
  • 43
0

I ran into the same problem recently. The only reason I wanted List<T> instead of T[] was because I wanted to add items to the array before sending a request to a web service.

I used the fact that xsd.exe generates a partial class. You can add your own partial class adding a constructor and an ADDT method that will use Array.Resize<T>() before assigning to the (new) last element.

There isn't any need to change the generated code or use another tool.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JacquesB.
  • 1
  • 1