Question

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/

Was it helpful?

Solution

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.

OTHER TIPS

Try using svcutil.exe

svcutil /o:myFile.cs /ct:System.Collections.Generic.List myXsd.xsd

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();

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top