SoapProxyPocoGenerator 0.4.0
dotnet add package SoapProxyPocoGenerator --version 0.4.0
NuGet\Install-Package SoapProxyPocoGenerator -Version 0.4.0
<PackageReference Include="SoapProxyPocoGenerator" Version="0.4.0" />
<PackageVersion Include="SoapProxyPocoGenerator" Version="0.4.0" />
<PackageReference Include="SoapProxyPocoGenerator" />
paket add SoapProxyPocoGenerator --version 0.4.0
#r "nuget: SoapProxyPocoGenerator, 0.4.0"
#:package SoapProxyPocoGenerator@0.4.0
#addin nuget:?package=SoapProxyPocoGenerator&version=0.4.0
#tool nuget:?package=SoapProxyPocoGenerator&version=0.4.0
SOAP Proxy POCO Generator
A .NET incremental source generator that automatically populates clean POCO (Plain Old CLR Object) domain models from SOAP proxy classes and generates corresponding AutoMapper profiles for seamless bidirectional mapping.
Table of Contents
- Features
- Installation
- Quick Start
- Configuration Options
- Generated Code
- Advanced Features
- XmlInclude Support
- Diagnostics
- Troubleshooting
- Sample Project
- Requirements
- Contributing
- License
- Acknowledgments
Features
- Domain-First Approach: Define your POCO classes in your domain layer and let the generator populate them from proxy classes
- Auto-Generated Attributes: All marker attributes are generated automatically - no separate attributes assembly required
- Automatic Property Generation: Extends partial POCO classes with properties from SOAP proxy classes without XML/SOAP attributes
- Smart Property Filtering: Automatically excludes properties with
[XmlIgnore], handles read-only properties, and supports type-safe exclusion configuration - Init-Only Property Support: Generates properties with
initaccessors for immutable domain models (C# 9+) - XML Attribute Support: Handles
XmlIgnore,XmlChoiceIdentifier,XmlAnyElement,XmlAnyAttribute, andXmlEnumattributes - Default Value Preservation: Optionally preserves
[DefaultValue]attributes from proxy properties - Required Attribute Support: Optionally preserves
[Required]attributes for validation frameworks - Comprehensive XmlInclude Support: Automatically detects and processes polymorphic type hierarchies from XmlInclude attributes on classes, interfaces, structs, and methods
- Inheritance Hierarchy Support: Handles complex multi-level inheritance chains with automatic derived type discovery
- Polymorphic Collections: Preserves polymorphic relationships in collections and properties
- AutoMapper Profile Generation: Creates bidirectional mappings for entire type hierarchies with proper ordering
- Incremental Generation: Efficient compilation with Roslyn's incremental generator pipeline
- Flexible Configuration: Attribute-based and property-level configuration options
- Comprehensive Diagnostics: Clear error messages and warnings with source location information for XmlInclude and configuration issues
- Type Safety: Preserves property types, nullability, collections, and generic types across polymorphic hierarchies
Installation
NuGet Package (Coming Soon)
dotnet add package SoapProxyPocoGenerator
Note: All attributes are auto-generated by the source generator. You do not need to reference a separate attributes assembly.
Manual Installation
- Clone or download this repository
- Add project reference to your solution:
<ItemGroup>
<ProjectReference Include="..\src\SoapProxyPocoGenerator\SoapProxyPocoGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
- Add AutoMapper to your project:
dotnet add package AutoMapper
Quick Start
1. Define Your POCO Classes
Create empty partial POCO classes in your domain layer and mark them with [MapFromProxy]:
// Note: The MapFromProxy attribute is auto-generated by the source generator
// No need to reference a separate attributes assembly!
namespace MyApp.Models
{
// Define your domain model and specify which proxy to map from
[MapFromProxy(typeof(VehicleProxy))]
public partial class Vehicle
{
// Properties will be automatically generated from VehicleProxy
}
[MapFromProxy(typeof(CarProxy))]
public partial class Car : Vehicle
{
// Properties will be automatically generated from CarProxy
}
[MapFromProxy(typeof(TruckProxy))]
public partial class Truck : Vehicle
{
// Properties will be automatically generated from TruckProxy
}
}
Important: POCO classes must be marked as partial to allow the generator to extend them with properties.
2. Your Proxy Classes
Your existing SOAP proxy classes remain unchanged:
using System.Xml.Serialization;
namespace MyApp.ProxyModels
{
[XmlInclude(typeof(CarProxy))]
[XmlInclude(typeof(TruckProxy))]
[XmlRoot("Vehicle")]
public class VehicleProxy
{
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Make")]
public string Make { get; set; }
[XmlElement("Model")]
public string Model { get; set; }
}
[XmlRoot("Car")]
public class CarProxy : VehicleProxy
{
[XmlElement("NumberOfDoors")]
public int NumberOfDoors { get; set; }
}
[XmlRoot("Truck")]
public class TruckProxy : VehicleProxy
{
[XmlElement("PayloadCapacity")]
public double PayloadCapacity { get; set; }
}
}
3. Build Your Project
The generator runs during compilation and creates:
- Partial class extensions with properties from proxy classes
- AutoMapper profile for bidirectional mapping
Generated partial class example:
// Auto-generated: Vehicle.g.cs
namespace MyApp.Models
{
public partial class Vehicle
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
}
4. Use Your POCOs
using AutoMapper;
using MyApp.Models;
using MyApp.ProxyModels;
// Configure AutoMapper with generated profile
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new VehicleMappingProfile());
});
IMapper mapper = config.CreateMapper();
// Create proxy instance (e.g., from SOAP service)
var carProxy = new CarProxy
{
Id = 1,
Make = "Toyota",
Model = "Camry",
NumberOfDoors = 4
};
// Map to POCO
var car = mapper.Map<Car>(carProxy);
// Map back to proxy
var mappedProxy = mapper.Map<CarProxy>(car);
Configuration Options
MapFromProxyAttribute
The [MapFromProxy] attribute is auto-generated by the source generator and applied to your POCO classes to specify which proxy class to generate properties from.
Constructor Parameter
[MapFromProxy(typeof(ProxyClassName))]
public partial class MyPoco
{
}
Parameter:
proxyType(Type, required): The proxy class type to generate properties from. Must be a valid class type accessible in the compilation.
Optional Properties
Namespace
Specify a custom namespace for generated code. If not specified, uses the POCO class namespace.
// Custom namespace
[MapFromProxy(typeof(VehicleProxy), Namespace = "MyApp.Domain.Models")]
public partial class Vehicle
{
}
// Default: uses POCO class namespace (MyApp.Models)
namespace MyApp.Models
{
[MapFromProxy(typeof(VehicleProxy))]
public partial class Vehicle
{
}
}
ExcludeProperties
Exclude specific properties from generation using type-safe property exclusion:
[MapFromProxy(
typeof(PersonProxy),
ExcludeProperties = new[] { "InternalId", "Metadata" }
)]
public partial class Person
{
// InternalId and Metadata will not be generated
}
Note: Property exclusions are type-safe - properties are associated with the specific proxy type, preventing accidental exclusions across different types.
IncludeReadOnlyProperties
Control whether read-only properties (properties with only a getter) are included:
[MapFromProxy(
typeof(PersonProxy),
IncludeReadOnlyProperties = true // Default: false
)]
public partial class Person
{
}
Default: false (read-only properties are excluded)
PreserveDefaultValues
Preserve default values from [DefaultValue] attributes on proxy properties:
[MapFromProxy(
typeof(ConfigProxy),
PreserveDefaultValues = true // Default: false
)]
public partial class Config
{
}
// If ConfigProxy has: [DefaultValue(10)] public int MaxRetries { get; set; }
// Generated: public int MaxRetries { get; set; } = 10;
Default: false (default values are not preserved)
PreserveRequiredAttributes
Preserve [Required] attributes from proxy properties for validation:
[MapFromProxy(
typeof(PersonProxy),
PreserveRequiredAttributes = true // Default: false
)]
public partial class Person
{
}
// If PersonProxy has: [Required] public string Name { get; set; }
// Generated: [Required] public string Name { get; set; }
Default: false (Required attributes are not preserved)
Property-Level Configuration
ExcludeMapAttribute
Apply to proxy class properties to exclude them from POCO generation:
// In your proxy class
public class PersonProxy
{
public string Name { get; set; }
[ExcludeMap] // Auto-generated attribute
public string InternalTrackingId { get; set; } // Will be excluded
public string Email { get; set; }
}
// In your POCO class
[MapFromProxy(typeof(PersonProxy))]
public partial class Person
{
// Only Name and Email will be generated
}
Note: ExcludeFromPocoAttribute is still supported but obsolete. Use ExcludeMapAttribute instead.
Automatic Property Filtering
The generator automatically excludes certain properties without explicit configuration:
XmlIgnore Support
Properties marked with [XmlIgnore] are automatically excluded:
public class PersonProxy
{
public string Name { get; set; }
[XmlIgnore]
public string InternalData { get; set; } // Automatically excluded
}
Read-Only Properties
Properties with only a getter are excluded by default (unless IncludeReadOnlyProperties = true):
public class PersonProxy
{
public string Name { get; set; }
public string FullName { get; } // Excluded by default
}
Init-Only Properties
Properties with init accessors are preserved:
public class PersonProxy
{
public string Id { get; init; } // Generated with init accessor
public string Name { get; set; }
}
// Generated POCO:
public partial class Person
{
public string Id { get; init; } // Immutable after initialization
public string Name { get; set; }
}
Complete Configuration Example
// Proxy class with various attributes
namespace MyApp.ProxyModels
{
public class OrderProxy
{
public int Id { get; set; }
public string OrderNumber { get; set; }
[ExcludeMap]
public string InternalReference { get; set; }
[XmlIgnore]
public string DebugInfo { get; set; }
[DefaultValue(10)]
public int MaxRetries { get; set; }
[Required]
public string CustomerName { get; set; }
public DateTime OrderDate { get; set; }
public string Status { get; } // Read-only
}
}
// POCO class with comprehensive configuration
namespace MyApp.Models
{
[MapFromProxy(
typeof(OrderProxy),
ExcludeProperties = new[] { "OrderDate" },
PreserveDefaultValues = true,
PreserveRequiredAttributes = true,
IncludeReadOnlyProperties = false
)]
public partial class Order
{
// Generated: Id, OrderNumber, MaxRetries = 10, [Required] CustomerName
// Excluded: InternalReference (ExcludeMap), DebugInfo (XmlIgnore),
// OrderDate (config), Status (read-only)
}
}
Generated Code
Partial POCO Classes
The generator creates partial class extensions that add properties from proxy classes without SOAP/XML attributes:
// Your code: Vehicle.cs
namespace MyApp.Models
{
[MapFromProxy(typeof(VehicleProxy))]
public partial class Vehicle
{
// Empty - properties will be generated
}
}
// Auto-generated: Vehicle.g.cs
namespace MyApp.Models
{
public partial class Vehicle
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
}
// Your code: Car.cs
namespace MyApp.Models
{
[MapFromProxy(typeof(CarProxy))]
public partial class Car : Vehicle
{
}
}
// Auto-generated: Car.g.cs
namespace MyApp.Models
{
public partial class Car
{
public int NumberOfDoors { get; set; }
}
}
AutoMapper Profiles
Generated profiles include bidirectional mappings:
// Auto-generated: VehicleMappingProfile.g.cs
using AutoMapper;
namespace MyApp.Models
{
public class VehicleMappingProfile : Profile
{
public VehicleMappingProfile()
{
CreateMap<MyApp.ProxyModels.VehicleProxy, MyApp.Models.Vehicle>().ReverseMap();
}
}
}
// Auto-generated: CarMappingProfile.g.cs
namespace MyApp.Models
{
public class CarMappingProfile : Profile
{
public CarMappingProfile()
{
CreateMap<MyApp.ProxyModels.CarProxy, MyApp.Models.Car>().ReverseMap();
}
}
}
Advanced Features
Inheritance Hierarchy Handling
The generator automatically discovers and processes entire inheritance hierarchies:
- Base Class Discovery: Traverses up the inheritance chain to find root types
- Derived Type Discovery: Extracts derived types from
XmlIncludeattributes - Recursive Processing: Processes all types in the hierarchy recursively
- Circular Reference Detection: Prevents infinite loops with visited type tracking
Complex Type Support
The generator handles various type scenarios:
- Collections:
List<T>,T[],IEnumerable<T>, etc. - Nullable Types:
int?,string?,Nullable<T> - Generic Types: Preserves generic type parameters
- Nested Proxy Types: Recursively generates POCOs for nested proxy classes
Example with Complex Types
// Proxy class
public class OrderProxy
{
public int Id { get; set; }
public List<OrderItemProxy> Items { get; set; } // Collection
public DateTime? ShippedDate { get; set; } // Nullable
public CustomerProxy Customer { get; set; } // Nested proxy
}
// Your POCO definition
[MapFromProxy(typeof(OrderProxy))]
public partial class Order
{
}
// Generated partial class
public partial class Order
{
public int Id { get; set; }
public List<OrderItem> Items { get; set; } // Collection preserved, mapped to OrderItem POCO
public DateTime? ShippedDate { get; set; } // Nullability preserved
public Customer Customer { get; set; } // Nested POCO reference
}
XmlInclude Support
The generator provides comprehensive support for polymorphic type hierarchies using the XmlInclude attribute. This feature automatically detects derived types and generates corresponding POCOs with proper inheritance relationships and AutoMapper mappings.
Overview
XmlInclude attributes can be applied to:
- Classes: Base classes with derived types
- Interfaces: Interfaces with multiple implementations
- Structs: Struct types with derived types
- Methods: Methods with polymorphic return types
The generator automatically:
- Detects XmlInclude attributes on type declarations and methods
- Validates that referenced types are actually derived from the base type
- Generates POCO classes for all types in the polymorphic hierarchy
- Creates AutoMapper mappings ordered from base to derived types
- Preserves polymorphic relationships in properties and collections
XmlInclude on Class Declarations
The most common scenario is applying XmlInclude to a base class to specify derived types:
// Proxy classes
using System.Xml.Serialization;
[XmlInclude(typeof(DogProxy))]
[XmlInclude(typeof(CatProxy))]
public class AnimalProxy
{
public string Name { get; set; }
public int Age { get; set; }
}
public class DogProxy : AnimalProxy
{
public string Breed { get; set; }
}
public class CatProxy : AnimalProxy
{
public bool IsIndoor { get; set; }
}
// POCO definitions
[MapFromProxy(typeof(AnimalProxy))]
public partial class Animal
{
}
[MapFromProxy(typeof(DogProxy))]
public partial class Dog : Animal
{
}
[MapFromProxy(typeof(CatProxy))]
public partial class Cat : Animal
{
}
Generated Output:
AnimalPOCO with Name and Age propertiesDogPOCO inheriting from Animal with Breed propertyCatPOCO inheriting from Animal with IsIndoor property- AutoMapper profile with mappings for all three types
XmlInclude on Interface Declarations
XmlInclude can be applied to interfaces to specify implementing types:
// Proxy interfaces and classes
[XmlInclude(typeof(CircleProxy))]
[XmlInclude(typeof(RectangleProxy))]
public interface IShapeProxy
{
double Area { get; }
}
public class CircleProxy : IShapeProxy
{
public double Radius { get; set; }
public double Area => Math.PI * Radius * Radius;
}
public class RectangleProxy : IShapeProxy
{
public double Width { get; set; }
public double Height { get; set; }
public double Area => Width * Height;
}
// POCO definitions
[MapFromProxy(typeof(IShapeProxy))]
public partial interface IShape
{
}
[MapFromProxy(typeof(CircleProxy))]
public partial class Circle : IShape
{
}
[MapFromProxy(typeof(RectangleProxy))]
public partial class Rectangle : IShape
{
}
XmlInclude on Method Return Types
XmlInclude can be applied to methods to specify polymorphic return types:
// Service proxy with polymorphic method
public class PaymentServiceProxy
{
[XmlInclude(typeof(CreditCardPaymentProxy))]
[XmlInclude(typeof(PayPalPaymentProxy))]
public PaymentProxy ProcessPayment(decimal amount)
{
// Implementation
}
}
public class PaymentProxy
{
public decimal Amount { get; set; }
public DateTime ProcessedDate { get; set; }
}
public class CreditCardPaymentProxy : PaymentProxy
{
public string CardNumber { get; set; }
}
public class PayPalPaymentProxy : PaymentProxy
{
public string Email { get; set; }
}
// POCO definitions
[MapFromProxy(typeof(PaymentProxy))]
public partial class Payment
{
}
[MapFromProxy(typeof(CreditCardPaymentProxy))]
public partial class CreditCardPayment : Payment
{
}
[MapFromProxy(typeof(PayPalPaymentProxy))]
public partial class PayPalPayment : Payment
{
}
The generator detects the XmlInclude attributes on the method and generates POCOs for all types in the hierarchy.
Polymorphic Collections
When a property contains a collection of a base type with XmlInclude attributes, the generator preserves the polymorphic relationship:
// Proxy classes
[XmlInclude(typeof(ManagerProxy))]
[XmlInclude(typeof(DeveloperProxy))]
public class EmployeeProxy
{
public string Name { get; set; }
public string Department { get; set; }
}
public class ManagerProxy : EmployeeProxy
{
public int TeamSize { get; set; }
}
public class DeveloperProxy : EmployeeProxy
{
public string ProgrammingLanguage { get; set; }
}
public class TeamProxy
{
public string TeamName { get; set; }
public List<EmployeeProxy> Members { get; set; } // Polymorphic collection
}
// POCO definitions
[MapFromProxy(typeof(EmployeeProxy))]
public partial class Employee
{
}
[MapFromProxy(typeof(ManagerProxy))]
public partial class Manager : Employee
{
}
[MapFromProxy(typeof(DeveloperProxy))]
public partial class Developer : Employee
{
}
[MapFromProxy(typeof(TeamProxy))]
public partial class Team
{
}
Generated Team POCO:
public partial class Team
{
public string TeamName { get; set; }
public List<Employee> Members { get; set; } // Can contain Manager or Developer instances
}
AutoMapper automatically handles polymorphic collection mapping, so the Members list can contain any Employee-derived type.
Multi-Level Inheritance Hierarchies
The generator supports complex multi-level hierarchies with XmlInclude at each level:
// Three-level hierarchy
[XmlInclude(typeof(TextDocumentProxy))]
public class DocumentProxy
{
public string Title { get; set; }
}
[XmlInclude(typeof(MarkdownDocumentProxy))]
public class TextDocumentProxy : DocumentProxy
{
public string Content { get; set; }
}
public class MarkdownDocumentProxy : TextDocumentProxy
{
public bool EnableTables { get; set; }
}
// POCO definitions
[MapFromProxy(typeof(DocumentProxy))]
public partial class Document
{
}
[MapFromProxy(typeof(TextDocumentProxy))]
public partial class TextDocument : Document
{
}
[MapFromProxy(typeof(MarkdownDocumentProxy))]
public partial class MarkdownDocument : TextDocument
{
}
The generator processes the entire hierarchy recursively, creating POCOs for all three levels with proper inheritance relationships.
AutoMapper Profile Generation
For polymorphic hierarchies, the generator creates AutoMapper profiles with mappings ordered from base to derived types:
// Generated AutoMapper profile
public class AnimalMappingProfile : Profile
{
public AnimalMappingProfile()
{
// Base type mapping first
CreateMap<AnimalProxy, Animal>().ReverseMap();
// Derived type mappings
CreateMap<DogProxy, Dog>().ReverseMap();
CreateMap<CatProxy, Cat>().ReverseMap();
}
}
This ordering ensures AutoMapper can properly handle polymorphic scenarios where a base type property may contain a derived type instance.
Limitations
While XmlInclude support is comprehensive, there are some limitations to be aware of:
1. XmlInclude on Properties Not Supported
XmlInclude attributes on properties are not part of the official .NET XML serialization API and are not supported by the generator:
// NOT SUPPORTED
public class ContainerProxy
{
[XmlInclude(typeof(DerivedProxy))] // This will be ignored
public BaseProxy Item { get; set; }
}
Workaround: Apply XmlInclude to the type declaration instead:
[XmlInclude(typeof(DerivedProxy))]
public class BaseProxy
{
// Properties
}
public class ContainerProxy
{
public BaseProxy Item { get; set; } // Polymorphism works via type-level XmlInclude
}
2. Generic Type Constraints
XmlInclude with open generic types has limitations:
// Limited support
[XmlInclude(typeof(GenericDerived<>))] // Open generic type
public class GenericBase<T>
{
public T Value { get; set; }
}
Workaround: Use closed generic types in XmlInclude:
[XmlInclude(typeof(GenericDerived<string>))]
[XmlInclude(typeof(GenericDerived<int>))]
public class GenericBase<T>
{
public T Value { get; set; }
}
3. Cross-Assembly References
XmlInclude types must be accessible in the compilation:
// Type must be in the same assembly or a referenced assembly
[XmlInclude(typeof(ExternalLibrary.DerivedType))] // Must reference ExternalLibrary
public class BaseProxy
{
}
Workaround: Ensure all referenced assemblies are properly included in your project references.
Best Practices
Apply XmlInclude to Base Types: Always apply XmlInclude attributes to the base class or interface, not to properties.
Define All Derived Types: Include all possible derived types in XmlInclude attributes to ensure complete POCO generation.
Use Consistent Naming: Follow consistent naming conventions for proxy and POCO classes to improve code readability.
Validate Hierarchies: Use the generator's diagnostics to identify and fix issues with type relationships.
Test Polymorphic Mappings: Write tests to verify AutoMapper correctly handles polymorphic scenarios in your domain.
Document Polymorphic Properties: Add comments to properties that can contain derived types to help other developers understand the polymorphic behavior.
Example: Complete Polymorphic Scenario
Here's a complete example demonstrating XmlInclude support with collections and multi-level hierarchies:
// Proxy classes
using System.Xml.Serialization;
[XmlInclude(typeof(PremiumAccountProxy))]
[XmlInclude(typeof(EnterpriseAccountProxy))]
public class AccountProxy
{
public string AccountId { get; set; }
public string Name { get; set; }
}
public class PremiumAccountProxy : AccountProxy
{
public decimal MonthlyFee { get; set; }
}
public class EnterpriseAccountProxy : PremiumAccountProxy
{
public int UserLimit { get; set; }
public string SupportLevel { get; set; }
}
public class OrganizationProxy
{
public string OrgName { get; set; }
public List<AccountProxy> Accounts { get; set; } // Polymorphic collection
}
// POCO definitions
[MapFromProxy(typeof(AccountProxy))]
public partial class Account
{
}
[MapFromProxy(typeof(PremiumAccountProxy))]
public partial class PremiumAccount : Account
{
}
[MapFromProxy(typeof(EnterpriseAccountProxy))]
public partial class EnterpriseAccount : PremiumAccount
{
}
[MapFromProxy(typeof(OrganizationProxy))]
public partial class Organization
{
}
// Usage
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AccountMappingProfile());
cfg.AddProfile(new OrganizationMappingProfile());
});
IMapper mapper = config.CreateMapper();
var orgProxy = new OrganizationProxy
{
OrgName = "Acme Corp",
Accounts = new List<AccountProxy>
{
new AccountProxy { AccountId = "1", Name = "Basic" },
new PremiumAccountProxy { AccountId = "2", Name = "Premium", MonthlyFee = 99.99m },
new EnterpriseAccountProxy
{
AccountId = "3",
Name = "Enterprise",
MonthlyFee = 499.99m,
UserLimit = 100,
SupportLevel = "24/7"
}
}
};
// Map to POCO - polymorphic types are preserved
var org = mapper.Map<Organization>(orgProxy);
// org.Accounts contains Account, PremiumAccount, and EnterpriseAccount instances
foreach (var account in org.Accounts)
{
Console.WriteLine($"{account.Name}: {account.GetType().Name}");
if (account is EnterpriseAccount enterprise)
{
Console.WriteLine($" Support: {enterprise.SupportLevel}");
}
}
XmlChoiceIdentifier Support
The generator handles discriminated union patterns using XmlChoiceIdentifier:
// Proxy with XmlChoice pattern
public class MessageProxy
{
[XmlChoiceIdentifier("ItemType")]
[XmlElement("String", typeof(string))]
[XmlElement("Number", typeof(int))]
public object Item { get; set; }
[XmlIgnore]
public ItemChoice ItemType { get; set; }
}
public enum ItemChoice
{
String,
Number
}
// Generated POCO preserves both properties
[MapFromProxy(typeof(MessageProxy))]
public partial class Message
{
public object Item { get; set; }
public ItemChoice ItemType { get; set; } // Included even with XmlIgnore
}
The generator emits an informational diagnostic (SPPG017) when it detects XmlChoice patterns.
XmlAnyElement and XmlAnyAttribute Support
Properties with XmlAnyElement or XmlAnyAttribute are included in generated POCOs:
// Proxy with extensibility support
public class ExtensibleProxy
{
public string Name { get; set; }
[XmlAnyElement]
public XmlElement[] ExtraElements { get; set; }
[XmlAnyAttribute]
public XmlAttribute[] ExtraAttributes { get; set; }
}
// Generated POCO includes XmlAny properties
[MapFromProxy(typeof(ExtensibleProxy))]
public partial class Extensible
{
public string Name { get; set; }
public XmlElement[] ExtraElements { get; set; }
public XmlAttribute[] ExtraAttributes { get; set; }
}
The generator emits an informational diagnostic (SPPG018) for XmlAny properties, as manual mapping may be required for XML types.
XmlEnum Support
Enum properties are correctly mapped even when XmlEnum attributes specify different serialization names:
// Proxy with enum
public enum StatusProxy
{
[XmlEnum("active")]
Active,
[XmlEnum("inactive")]
Inactive,
[XmlEnum("pending")]
Pending
}
public class OrderProxy
{
public StatusProxy Status { get; set; }
}
// Generated POCO preserves enum type
[MapFromProxy(typeof(OrderProxy))]
public partial class Order
{
public StatusProxy Status { get; set; } // Enum type preserved
}
The generator copies enum types as-is without attempting to rename values based on XmlEnum attributes.
Diagnostics
The generator provides comprehensive diagnostics to help identify and resolve issues:
SPPG001: Unsupported Type
Severity: Warning
Description: A type cannot be mapped to a POCO type (e.g., pointer types, ref structs).
Example:
warning SPPG001: Type 'System.IntPtr' is not supported for POCO generation. Pointer types cannot be used in POCOs.
Resolution: Remove or replace unsupported types with compatible alternatives.
SPPG002: Circular Reference Detected
Severity: Warning
Description: A circular reference was detected in the inheritance hierarchy or type relationships.
Example:
warning SPPG002: Circular reference detected in type 'MyApp.ProxyModels.NodeProxy'. The type will be skipped to prevent infinite recursion.
Resolution: Review your type hierarchy and break circular dependencies. Consider using interfaces or restructuring relationships.
SPPG003: Invalid Configuration
Severity: Error
Description: Configuration provided in GeneratePocoAttribute is invalid.
Example:
error SPPG003: Invalid configuration: Namespace cannot be empty or whitespace.
Resolution: Check attribute parameters and ensure they contain valid values.
SPPG004: XmlInclude Type Not Found
Severity: Warning
Description: A type specified in an XmlInclude attribute could not be resolved.
Example:
warning SPPG004: Type 'MyApp.ProxyModels.MissingProxy' specified in XmlInclude attribute on 'VehicleProxy' was not found or could not be resolved.
Resolution: Ensure all types referenced in XmlInclude attributes exist and are accessible in the compilation.
SPPG005: Property Type Mapping Warning
Severity: Info
Description: A property type may not map exactly to the POCO representation.
Example:
info SPPG005: Property 'ComplexData' on type 'MyProxy' has type 'System.Object' which may not map correctly to POCO. Consider using a more specific type.
Resolution: Review the generated code and consider using more specific types for better type safety.
SPPG006: Proxy Type Not Found
Severity: Error
Description: The proxy type specified in MapFromProxy attribute cannot be found.
Example:
error SPPG006: Proxy type 'MyApp.ProxyModels.MissingProxy' specified in MapFromProxy attribute was not found or could not be resolved.
Resolution: Ensure the proxy type exists and is accessible. Check namespace and assembly references.
SPPG007: Invalid Proxy Type
Severity: Error
Description: The proxy type parameter is not a valid class type.
Example:
error SPPG007: Type 'System.Int32' is not a valid proxy class type. The proxy type must be a class.
Resolution: Ensure the MapFromProxy attribute references a class type, not a value type or interface.
SPPG008: POCO Class Not Partial
Severity: Warning
Description: A POCO class with MapFromProxy is not declared as partial.
Example:
warning SPPG008: Class 'Vehicle' should be declared as partial to allow property generation.
Resolution: Add the partial keyword to your POCO class declaration:
[MapFromProxy(typeof(VehicleProxy))]
public partial class Vehicle // Add 'partial' keyword
{
}
SPPG009: Invalid Namespace
Severity: Error
Description: The namespace specified in MapFromProxy is not valid.
Example:
error SPPG009: Namespace '123Invalid' is not a valid C# namespace identifier.
Resolution: Use a valid C# namespace following naming conventions (letters, numbers, underscores, dots).
SPPG010: Base POCO Class Missing
Severity: Warning
Description: A base POCO class is expected but not found for a proxy base type.
Example:
warning SPPG010: Base POCO class 'Vehicle' is expected but not found for proxy base type 'VehicleProxy'. Ensure the POCO class inherits from the expected base class.
Resolution: Ensure your POCO class hierarchy matches the proxy class hierarchy:
[MapFromProxy(typeof(VehicleProxy))]
public partial class Vehicle { }
[MapFromProxy(typeof(CarProxy))]
public partial class Car : Vehicle { } // Must inherit from Vehicle
SPPG011: XmlInclude Type Not Derived
Severity: Warning
Description: An XmlInclude attribute references a type that is not derived from the declaring type.
Example:
warning SPPG011: Type 'DogProxy' in XmlInclude attribute is not derived from 'VehicleProxy'
Resolution: Ensure the type in XmlInclude actually inherits from the base type:
[XmlInclude(typeof(CarProxy))] // CarProxy must inherit from VehicleProxy
public class VehicleProxy
{
}
public class CarProxy : VehicleProxy // Correct inheritance
{
}
SPPG012: XmlInclude On Void Method
Severity: Warning
Description: XmlInclude attribute is applied to a method with void return type.
Example:
warning SPPG012: XmlInclude attribute on method 'ProcessData' with void return type will be ignored
Resolution: Remove the XmlInclude attribute from void methods, or change the method to return a type:
// Remove XmlInclude from void methods
public void ProcessData() { }
// Or change to return a type
[XmlInclude(typeof(DerivedResult))]
public ResultProxy ProcessData() { }
SPPG013: Duplicate XmlInclude Type
Severity: Info
Description: The same type is referenced in multiple XmlInclude attributes.
Example:
info SPPG013: Type 'DogProxy' is referenced multiple times in XmlInclude attributes on 'AnimalProxy'
Resolution: Remove duplicate XmlInclude attributes:
// Before
[XmlInclude(typeof(DogProxy))]
[XmlInclude(typeof(CatProxy))]
[XmlInclude(typeof(DogProxy))] // Duplicate
public class AnimalProxy { }
// After
[XmlInclude(typeof(DogProxy))]
[XmlInclude(typeof(CatProxy))]
public class AnimalProxy { }
SPPG014: XmlInclude Type Not Derived From Return Type
Severity: Warning
Description: An XmlInclude attribute on a method references a type that is not derived from the method return type.
Example:
warning SPPG014: Type 'DogProxy' in XmlInclude attribute is not derived from method return type 'VehicleProxy'
Resolution: Ensure the XmlInclude type inherits from the method return type:
[XmlInclude(typeof(CarProxy))] // CarProxy must inherit from VehicleProxy
public VehicleProxy GetVehicle()
{
return new CarProxy();
}
public class CarProxy : VehicleProxy // Correct inheritance
{
}
SPPG015: Property Excluded Due to XmlIgnore
Severity: Info
Description: A property was automatically excluded because it has the [XmlIgnore] attribute.
Example:
info SPPG015: Property 'InternalData' excluded from POCO generation due to [XmlIgnore] attribute
Resolution: This is informational. If you want to include the property, remove the [XmlIgnore] attribute from the proxy class.
SPPG016: Property Excluded Due to Read-Only
Severity: Info
Description: A read-only property was excluded from POCO generation.
Example:
info SPPG016: Property 'FullName' excluded from POCO generation because it is read-only. Set IncludeReadOnlyProperties=true to include it.
Resolution: Set IncludeReadOnlyProperties = true in the MapFromProxy attribute if you want to include read-only properties:
[MapFromProxy(typeof(PersonProxy), IncludeReadOnlyProperties = true)]
public partial class Person { }
SPPG017: XmlChoice Pattern Detected
Severity: Info
Description: A property uses XmlChoiceIdentifier for discriminated union patterns.
Example:
info SPPG017: Property 'Item' uses XmlChoiceIdentifier with discriminator 'ItemType'
Resolution: This is informational. The generator will include both the choice property and discriminator property in the POCO.
SPPG018: XmlAny Property Detected
Severity: Info
Description: A property uses XmlAnyElement or XmlAnyAttribute for extensibility.
Example:
info SPPG018: Property 'ExtraElements' uses XmlAnyElement or XmlAnyAttribute. Manual mapping may be required for XML types.
Resolution: This is informational. The property will be included in the POCO, but you may need to handle XML type mapping manually in your AutoMapper configuration.
SPPG019: Invalid Default Value
Severity: Warning
Description: A default value specified in a [DefaultValue] attribute is not compatible with the property type.
Example:
warning SPPG019: Default value 'abc' for property 'Count' is not compatible with type 'int'
Resolution: Fix the default value in the proxy class to match the property type:
// Wrong
[DefaultValue("abc")]
public int Count { get; set; }
// Correct
[DefaultValue(0)]
public int Count { get; set; }
Troubleshooting
Generated Files Not Appearing
Problem: POCOs and AutoMapper profiles are not being generated.
Solutions:
- Ensure the generator project is referenced with
OutputItemType="Analyzer" - Clean and rebuild your solution
- Check that proxy classes have the
[GeneratePoco]attribute - Verify the generator project targets .NET Standard 2.0
- Check the build output for diagnostic messages
Compilation Errors in Generated Code
Problem: Generated code causes compilation errors.
Solutions:
- Check for SPPG diagnostics in the Error List
- Ensure all proxy types are accessible (public visibility)
- Verify namespace configuration is valid
- Check for naming conflicts with existing types
AutoMapper Configuration Errors
Problem: AutoMapper throws configuration errors at runtime.
Solutions:
- Ensure you've added the generated profile to AutoMapper configuration:
cfg.AddProfile(new YourProxyMappingProfile()); - Verify AutoMapper package is installed
- Check that POCO and proxy types are accessible
- Use
config.AssertConfigurationIsValid()to identify mapping issues
Missing Properties in Generated POCOs
Problem: Some properties are not appearing in generated POCOs.
Solutions:
- Check if properties are marked with
[ExcludeFromPoco] - Verify properties are not listed in
ExcludePropertiesattribute parameter - Ensure properties are public (private/internal properties are excluded)
- Check for SPPG005 diagnostics about unsupported property types
Inheritance Not Working Correctly
Problem: Derived types are not being generated or inheritance is broken.
Solutions:
- Ensure base classes have
[XmlInclude]attributes for all derived types - Verify all types in the hierarchy are accessible
- Check for SPPG002 circular reference warnings
- Ensure derived types are in the same assembly or referenced assemblies
Performance Issues During Build
Problem: Build times are significantly slower after adding the generator.
Solutions:
- The generator uses incremental generation - only changed files are reprocessed
- Reduce the number of proxy classes marked with
[GeneratePoco] - Check for circular references that may cause repeated processing
- Ensure you're using the latest version of the generator
XmlInclude Types Not Being Generated
Problem: POCOs for derived types referenced in XmlInclude are not being generated.
Solutions:
- Verify the XmlInclude attribute syntax is correct:
[XmlInclude(typeof(DerivedProxy))] // Correct public class BaseProxy { } - Ensure the derived type is accessible (public visibility)
- Check for SPPG004 diagnostics indicating the type cannot be found
- Verify the derived type actually inherits from the base type
- Ensure you have MapFromProxy attributes on all POCO classes in the hierarchy
- Check that the derived type is in the same assembly or a referenced assembly
XmlInclude Diagnostics (SPPG011, SPPG014)
Problem: Getting warnings about XmlInclude types not being derived from base type.
Solutions:
- Verify the inheritance relationship:
[XmlInclude(typeof(DogProxy))] public class AnimalProxy { } public class DogProxy : AnimalProxy { } // Must inherit - For method XmlInclude, ensure the type inherits from the return type:
[XmlInclude(typeof(CarProxy))] public VehicleProxy GetVehicle() { } public class CarProxy : VehicleProxy { } // Must inherit from return type - Check for typos in type names
- Ensure you're not mixing unrelated type hierarchies
Polymorphic Collections Not Mapping Correctly
Problem: AutoMapper fails to map collections with polymorphic elements.
Solutions:
- Ensure all types in the hierarchy have generated AutoMapper profiles
- Register all profiles with AutoMapper:
var config = new MapperConfiguration(cfg => { cfg.AddProfile(new BaseTypeMappingProfile()); cfg.AddProfile(new DerivedType1MappingProfile()); cfg.AddProfile(new DerivedType2MappingProfile()); }); - Verify the base type has XmlInclude attributes for all derived types
- Check that POCO inheritance matches proxy inheritance
- Use
config.AssertConfigurationIsValid()to identify mapping issues
XmlInclude on Methods Not Working
Problem: XmlInclude attributes on methods are being ignored.
Solutions:
- Ensure the method has a non-void return type:
// Won't work - void return [XmlInclude(typeof(DerivedProxy))] public void ProcessData() { } // Works - returns a type [XmlInclude(typeof(DerivedProxy))] public BaseProxy ProcessData() { } - Check for SPPG012 diagnostics about void methods
- Verify the XmlInclude types inherit from the method return type
- Ensure the method is public and accessible
Multi-Level Hierarchies Not Fully Generated
Problem: Only some levels of a multi-level hierarchy are being generated.
Solutions:
- Ensure XmlInclude attributes are present at each level:
[XmlInclude(typeof(MiddleProxy))] public class BaseProxy { } [XmlInclude(typeof(DerivedProxy))] // Don't forget this! public class MiddleProxy : BaseProxy { } public class DerivedProxy : MiddleProxy { } - Define MapFromProxy for all levels:
[MapFromProxy(typeof(BaseProxy))] public partial class Base { } [MapFromProxy(typeof(MiddleProxy))] public partial class Middle : Base { } [MapFromProxy(typeof(DerivedProxy))] public partial class Derived : Middle { } - Check for circular reference warnings (SPPG002)
- Verify all types are accessible and public
XmlInclude on Interfaces Not Working
Problem: XmlInclude on interfaces is not generating POCOs for implementing types.
Solutions:
- Ensure the interface is marked with XmlInclude:
[XmlInclude(typeof(Implementation1Proxy))] [XmlInclude(typeof(Implementation2Proxy))] public interface IBaseProxy { } - Verify implementing types actually implement the interface:
public class Implementation1Proxy : IBaseProxy { } - Define MapFromProxy for the interface and all implementations
- Check that the interface and implementations are public
Duplicate Type Warnings (SPPG013)
Problem: Getting informational diagnostics about duplicate XmlInclude references.
Solutions:
- Remove duplicate XmlInclude attributes:
// Before [XmlInclude(typeof(DogProxy))] [XmlInclude(typeof(CatProxy))] [XmlInclude(typeof(DogProxy))] // Duplicate // After [XmlInclude(typeof(DogProxy))] [XmlInclude(typeof(CatProxy))] - This is informational only - the generator will process the type once
- Clean up duplicates to improve code clarity
Generic Types with XmlInclude
Problem: XmlInclude with generic types is not working as expected.
Solutions:
- Use closed generic types in XmlInclude:
// Won't work well [XmlInclude(typeof(GenericDerived<>))] // Works better [XmlInclude(typeof(GenericDerived<string>))] [XmlInclude(typeof(GenericDerived<int>))] public class GenericBase<T> { } - Define separate MapFromProxy for each closed generic type
- Consider using non-generic base types for polymorphic scenarios
- Check generator diagnostics for specific issues with generic types
Sample Project
The solution includes comprehensive sample projects demonstrating various features:
Main Sample Project
- Proxy classes with inheritance hierarchies
- POCO generation configuration
- AutoMapper profile usage
- Bidirectional mapping examples
XmlInclude Samples
Located in src/SampleProject/XmlIncludeSamples/, this directory contains detailed examples of XmlInclude support:
- Class Declaration Samples: XmlInclude on base classes with multiple derived types
- Interface Declaration Samples: XmlInclude on interfaces with multiple implementations
- Method Return Type Samples: XmlInclude on methods with polymorphic return types
- Polymorphic Collection Samples: Collections containing polymorphic elements
- Multi-Level Inheritance Samples: Three-level inheritance hierarchies with XmlInclude at each level
- MapFromProxy Approach: Recommended pattern using explicit POCO definitions
Each sample includes:
- Proxy class definitions with XmlInclude attributes
- POCO class definitions with MapFromProxy attributes
- Generated code examples
- Usage demonstrations
To run the samples:
cd src/SampleProject
dotnet build
dotnet run
To view generated files, check the obj/GeneratedFiles/ directory after building.
For detailed documentation on XmlInclude samples, see src/SampleProject/XmlIncludeSamples/README.md.
Requirements
- .NET 8.0 or later (for consuming projects)
- .NET Standard 2.0 (for generator library)
- AutoMapper 12.0.0 or later (runtime dependency)
- Microsoft.CodeAnalysis.CSharp 4.8.0 or later (generator dependency)
Contributing
We welcome contributions from the community! Whether you're fixing bugs, adding features, improving documentation, or reporting issues, your help is appreciated.
How to Contribute
- Fork the repository and create your branch from
main - Make your changes following our coding standards
- Add tests for new functionality
- Ensure all tests pass by running
dotnet test - Update documentation as needed
- Submit a pull request with a clear description of your changes
Please read our Contributing Guidelines for detailed information on:
- Development setup
- Code style and conventions
- Testing guidelines
- Pull request process
- Reporting issues
Publishing to NuGet
For maintainers publishing new versions:
- 📦 Publishing Guide - Complete guide with quick start section
Code of Conduct
This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.
Getting Help
- 📖 Read the documentation
- 🐛 Report bugs via GitHub Issues
- 💡 Request features via GitHub Issues
- 💬 Ask questions in GitHub Discussions
License
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2025 SOAP Proxy POCO Generator Contributors
Acknowledgments
Built With
- Roslyn - .NET Compiler Platform for source generation
- AutoMapper - Object-to-object mapping library
Inspiration
This project was created to solve the common problem of maintaining clean domain models while working with SOAP web services. By reversing the traditional code generation approach, developers can define their domain models explicitly and let the generator handle the tedious property copying.
Contributors
Thank you to all the contributors who have helped make this project better! 🎉
Special Thanks
- The .NET team for creating the powerful Roslyn compiler platform
- The AutoMapper team for their excellent mapping library
- The open-source community for inspiration and support
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.4.0 | 179 | 11/2/2025 |
Version 0.1.0 - Initial release with domain-first approach, auto-generated attributes, comprehensive XML serialization support, polymorphic type hierarchies, and AutoMapper profile generation. See CHANGELOG.md for full feature list.