Generate Faster Mono XML Serializers with sgen
sgen pre-generates the XML serializer that XmlSerializer would otherwise build at runtime, trading a slow first request for a build-time step. You will finish with a generated Assembly.XmlSerializers.dll sitting beside your Mono assembly, ready to load. This guide uses sgen from Mono 6.8.0.105 on the local system, whose tool reports runtime version 4.0.30319.42000. Allow about fifteen minutes if your assembly is already built. You need a readable .NET assembly, a shell, and the mono-devel package. The commands write files to a chosen directory; nothing here installs or registers anything system-wide.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Confirm the installed tool
- 2. Build or locate the input assembly
- 3. Generate the serializer in a separate directory
- 4. Select a type only when you need to
- 5. Handle references and compiler options carefully
- 6. Avoid accidental replacement
- 7. Keep scripts quiet without hiding failures
- 8. Know the boundary of this tool
Checkpoint
Keep the original assembly and pick an empty output directory before generating. Do not point the command at a directory holding a serializer you have not backed up. Without the force option, sgen refuses to overwrite existing output, which is the safe default.
1. Confirm the installed tool
Check the command is available and note the package version:
$ command -v sgen
/usr/bin/sgen
$ dpkg-query -W -f='${Package} ${Version}\n' mono-devel
mono-devel 6.8.0.105+dfsg-3.6ubuntu2
$ sgen --help
Mono Xml Serializer Generator Tool
Mono version 4.0.30319.42000
The installed help output is brief, so lean on the manpage for what the options actually mean. The relevant inputs are an assembly and, where needed, its referenced assemblies. The tool writes a separate serializer assembly rather than touching the input DLL.
2. Build or locate the input assembly
sgen needs a compiled assembly, not a C# source file. For a quick self-contained test, build a small library in a working directory:
$ mkdir -p ~/sgen-demo
$ cd ~/sgen-demo
$ mcs -target:library -out:Widget.dll Widget.cs
Use your own existing DLL if you have one. A minimal Widget class with a public Name property is enough to demonstrate the command, but the case that actually matters in production is an assembly containing the serializable types your application uses. Check the path before continuing:
$ test -r Widget.dll && file Widget.dll
Widget.dll: PE32 executable (DLL) ...
If that check fails, stop and fix the path or read permission. Do not run the generator as root just to force a badly located file to work.
3. Generate the serializer in a separate directory
Create a destination and pass the assembly with the long option form: the colon sits between the option and its value here, not a space.
$ mkdir generated
$ sgen --nologo --assembly:Widget.dll --out:generated
Generated assembly: Widget.XmlSerializers.dll
$ find generated -maxdepth 1 -type f -printf '%f\n'
Widget.XmlSerializers.dll
The default output directory is the current one, but an explicit --out keeps generated artefacts away from source and build files. The output name comes from the input assembly, so Orders.dll gets you Orders.XmlSerializers.dll.
Checkpoint
Verify the output exists and is non-empty:
$ test -s generated/Widget.XmlSerializers.dll && echo 'serializer generated'
serializer generated
A success message only tells you generation completed. Keep this DLL paired with the matching input assembly and test it through the application that will actually load it.
4. Select a type only when you need to
By default, sgen processes the whole assembly. Use --type when you need one runtime type named explicitly:
$ mkdir selected
$ sgen --nologo --assembly:Widget.dll --type:Widget --out:selected
Generated assembly: Widget.XmlSerializers.dll
The value is the runtime type name, not a source filename. For a namespaced class, use its fully qualified name, such as Example.Models.Order. If the type cannot be resolved or the assembly cannot be loaded, fix the assembly and dependency paths first; do not assume a generated DLL is valid just because a file landed on disk.
5. Handle references and compiler options carefully
If the target assembly refers to another assembly the generator cannot resolve, supply that dependency with --reference:
$ sgen --nologo \
--assembly:Orders.dll \
--reference:Contracts.dll \
--out:generated
- Repeat the option for each referenced assembly your build actually needs.
- Match build versions. Keep dependency files from the same build as the target; mixing versions can produce a serializer that compiles but does not match the application at runtime.
--compilerpasses options through to the generation step. Treat it as a build-system setting, not a place for shell syntax, and quote values by the command's own argument rules.--debugasks the compiler for debug information.--keepkeeps temporary generated source for inspection, handy when diagnosing a failed build but liable to leave extra files behind.
6. Avoid accidental replacement
Running the same generation command twice does not silently replace the serializer. It errors out instead:
$ sgen --nologo --assembly:Widget.dll --out:generated
Cannot generate assembly '.../generated/Widget.XmlSerializers.dll' because it already exist. Use /force option to overwrite the existing assembly
That protection earns its keep: overwriting a known-good serializer can break a build or leave you unsure which generated file was actually tested. Generate to a new directory first, compare or test the result, then update the build output the normal way.
Use --force only when replacement is deliberate:
$ sgen --nologo --force \
--assembly:Widget.dll \
--out:generated
Warning
Force overwrites the existing generated assembly. Back it up or generate into a new directory if you might need to roll back. If a forced run produces a bad result, restore the backup or replace the destination with the previously verified file from version control: sgen has no undo command.
7. Keep scripts quiet without hiding failures
--silent suppresses normal progress output; --verbose asks for more. Neither changes the generated serializer. In a script, preserve and check the exit status:
if sgen --silent --assembly:Widget.dll --out:generated; then
test -s generated/Widget.XmlSerializers.dll
else
status=$?
printf 'sgen failed with status %s\n' "$status" &2
exit "$status"
fi
Do not treat silence as success, and do not ignore a non-zero exit status. Add a separate file check too, since a later build step can remove or replace the generated file.
8. Know the boundary of this tool
--proxytypes is listed in the installed manpage but marked not supported yet, so do not build a workflow around it. The tool generates serializer classes for the older Mono 2.0 profile family the manual describes; exact runtime and compiler compatibility still depends on the target assembly and the rest of your build. Test the generated DLL with the actual application and framework profile that will load it.
Nothing here needs elevated privileges. Avoid sudo: it can leave generated files root-owned and mask an ordinary path or permission mistake. If your build directory is not writable, pick a writable working directory or fix ownership through your normal administration process.
Done means
- Versions identified:
sgenand themono-develpackage. - Input assembly untouched and readable by the invoking user.
- Matching output generated: a
*.XmlSerializers.dllin a deliberate output directory. - Existing output protected unless
--forcewas an explicit, backed-up decision. - Extras used only when needed: references, type selection and compiler settings supplied only when the assembly required them.
- Generated file tested for existence, then run through the actual application build.