Generate Mono XML Serialisers with genxs
Before you build a pipeline around genxs, find out whether your Mono install can still run this old generator at all. Then write a configuration file that names a .NET class and assembly, picks the generated reader and writer names, and leaves hooks for custom code. Allow about twenty minutes for a first configuration.
The route
Jump straight to the step you need, or tick off Done means at the end.
- You need: Mono's development tools, a compiled assembly containing the target class, and write access to a separate output directory.
- You should know: enough about the class's XML serialisation behaviour to review generated C#.
- Target: the Debian or Ubuntu
mono-develpackage and its installedgenxswrapper. - Privileges: this is code generation, not server configuration, so it normally needs no elevation.
1. Check the installed tool
Record the binary and package version. These are ordinary read-only commands:
$ command -v genxs
/usr/bin/genxs
$ dpkg-query -W -f='${Package} ${Version}\n' mono-devel
mono-devel 6.8.0.105+dfsg-3.6ubuntu2
On this installation, /usr/bin/genxs invokes Mono with /usr/lib/mono/4.5/genxs.exe. The manual describes genxs as Mono-only because it uses internal runtime classes. Do not assume a different .NET runtime, or a newer replacement tool, shares the same configuration format.
Checkpoint
Run a harmless probe and save the result. Expect this on the guide machine:
$ genxs
An error occurred while generating serializers: System.Exception: This runtime does not support generation of serializers
The exact stack trace can vary. The installed command on this host says it cannot generate serializers, even before you give it a configuration file. Treat that as a prerequisite failure. A successful file write or an empty output directory does not mean serialisers were generated.
Warning
If your host reports the same error, use a supported Mono environment or stop here and sort out the runtime before you wire genxs into a build.
2. Prepare an assembly and an output directory
genxs reads a compiled assembly. It does not compile the class named by class. Replace the placeholder paths with real ones from your project, then create a fresh directory for generated files:
$ assembly_path="$PWD/build/Example.Models.dll"
$ output_dir="$PWD/build/generated"
$ test -r "$assembly_path" && echo "assembly is readable"
assembly is readable
$ mkdir -p "$output_dir"
$ find "$output_dir" -maxdepth 1 -type f -print
The final command should print nothing for a fresh directory. Keep generated output out of your source directory until you have reviewed it.
The destination argument is positional, so the command shape is genxs CONFIGURATION_FILE DESTINATION_FOLDER. Leave the destination out and genxs uses its own default behaviour. Give it an explicit directory instead of building a script around an undocumented default.
Checkpoint
Confirm the assembly really contains the fully qualified class name you plan to use. A metadata tool such as monodis may help, though its output is outside genxs's contract. The configuration name must include its namespace, for example Example.Models.Order.
3. Write the smallest useful configuration
Create genxs.xml beside the build script. This example asks for both generated classes and chooses their namespace and output file:
<configuration>
<serializer class="Example.Models.Order" assembly="/absolute/path/to/Example.Models.dll">
<reader>OrderReader</reader>
<writer>OrderWriter</writer>
<namespace>Example.Generated</namespace>
<outFileName>OrderSerialiser.cs</outFileName>
</serializer>
</configuration>
Use an absolute assembly path while you get the configuration working. The manual permits a complete path, and it removes any doubt about the current working directory. You can put several serializer elements under configuration when one run should cover several classes.
Make these defaults explicit:
readerandwriter: the names of the generated classes.namespace: the namespace of the generated reader and writer.outFileName: the generated file name.noReaderandnoWriter: disable one side when set totrue. Do not pair a disabled side with a build step that expects its class to exist.baseSerializerandimplementation: marked as version 2.0-only in this installed manual, so test them against the exact Mono version your build uses.
4. Run generation safely
Run genxs with the configuration and the isolated destination:
$ genxs "$PWD/genxs.xml" "$output_dir"
$ status=$?
$ printf 'genxs exit status: %s\n' "$status"
genxs exit status: 0
A supported runtime may print no success message. The useful results are the exit status and the files in the destination:
$ find "$output_dir" -maxdepth 1 -type f -printf '%f\n'
OrderSerialiser.cs
The file name and generated class content depend on your configuration. Open the C# file and compile it in the same project or a suitable generated-code project. Do not paste generated text into a hand-maintained file without a clear reason: the point of genxs is that you can recreate the output after the source assembly changes.
Warning
A destination that already holds a file with the same name is a state-changing target. Preserve the old output before a deliberate replacement:
$ cp --preserve=all "$output_dir/OrderSerialiser.cs" "$output_dir/OrderSerialiser.cs.bak"
$ genxs "$PWD/genxs.xml" "$output_dir.new"
$ find "$output_dir.new" -maxdepth 1 -type f -print
Review and compile the new file before you replace the old directory. If generation fails, remove only the new temporary directory and keep the backup.
Destructive action
That cleanup is optional and destructive, so inspect the path before you use rm -rf.
5. Add hooks for what generated code cannot infer
Hooks are XML children under readerHooks or writerHooks. A hook can select a type, an attribute or a member, then replace the normal operation or insert code before or after it. This example validates every object after a reader has deserialised it:
<readerHooks>
<hook type="type">
<insertAfter>
Example.Validation.Validate($OBJECT);
</insertAfter>
</hook>
</readerHooks>
For a narrower hook, add a select element:
typeName: must include the namespace.typeAttribute: selects classes carrying a full attribute name.typeMember: selects a member name.
The hook type can be attributes, elements, unknownAttribute, unknownElement, member or type.
genxs exposes special variables while it writes code:
$TYPEand$FULLTYPE: identify the selected class.$OBJECT: the object being processed.$ELEMENT,$NAMESPACEand$MEMBER: describe the current XML or member context where applicable.
Tip
A type-level reader hook using replace must assign the deserialised object to $OBJECT. A typo in hook source becomes a generated C# error, so compile straight after changing a hook.
6. Diagnose failures in the right layer
A non-zero exit status can come from an unreadable assembly, an invalid XML configuration, an unmatched class name or missing runtime support. Check in that order:
- Assembly:
test -ron the file. - Configuration: run it through an XML parser.
- Type: the fully qualified class name and assembly identity.
- Runtime: whether it supports serialiser generation at all.
Running as root does not repair a missing type or add serialiser support.
If a reader or writer is absent, inspect noReader and noWriter before changing hook code. If the output compiles but behaves wrongly, compare the generated class names, namespace and hook selection with the configuration. Keep the assembly, configuration and generated file from the same build revision, so a stale output file cannot hide a source change.
Done means
- Tool checked: the exact Mono package and genxs wrapper were recorded before any configuration work.
- Runtime proven: the probe passed, or its serialiser-support failure was treated as a blocker.
- Configuration real: it names a genuine fully qualified class and a readable assembly.
- Output isolated: generation targets its own directory and the result compiles.
- Hooks scoped: they use the documented selectors and regenerate cleanly from source.
- Backups made: existing generated files were saved before any deliberate replacement.