How to check if a class implements interface methods in NRefactory - c#

I have two file. One of this is a class declaration and other is interface declaration. Class should implements interface. How can I check in NRefactory if class implements interface methods?
I should give more details.
First file - for example:
class Test : IF
{
}
and the second
interface IF
{
void Foo();
}
I have to read these files and parse with NRefactory. I need to check if class Test implements method from interface IF.
Without compilation and loading compiled assembly.

Use the is keyword
http://msdn.microsoft.com/en-us/library/scekt9xw.aspx
if (myObj is IMyInterface) {
....
}

I found solution in NRefactory code. I've modified this in order to achive my goals. First we should implement visitor which check if classes implements each method from interface:
public class MissingInterfaceMemberImplementationVisitor : DepthFirstAstVisitor
{
private readonly CSharpAstResolver _resolver;
public bool IsInterfaceMemberMissing { get; private set; }
public MissingInterfaceMemberImplementationVisitor(CSharpAstResolver resolver)
{
_resolver = resolver;
IsInterfaceMemberMissing = false;
}
public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
if (typeDeclaration.ClassType == ClassType.Interface || typeDeclaration.ClassType == ClassType.Enum)
return;
base.VisitTypeDeclaration(typeDeclaration);
var rr = _resolver.Resolve(typeDeclaration);
if (rr.IsError)
return;
foreach (var baseType in typeDeclaration.BaseTypes)
{
var bt = _resolver.Resolve(baseType);
if (bt.IsError || bt.Type.Kind != TypeKind.Interface)
continue;
bool interfaceMissing;
var toImplement = ImplementInterfaceAction.CollectMembersToImplement(rr.Type.GetDefinition(), bt.Type, false, out interfaceMissing);
if (toImplement.Count == 0)
continue;
IsInterfaceMemberMissing = true;
}
}
}
And now we have to read all files, parse them and invoke above class in a following way:
var solutionFiles = new List<FileInfo>();
var trees = new Dictionary<FileInfo, SyntaxTree>();
IProjectContent projectContent = new CSharpProjectContent();
foreach (var file in solutionFiles.Where(f => f.Extension == ".cs").Distinct())
{
var parser = new ICSharpCode.NRefactory.CSharp.CSharpParser();
SyntaxTree syntaxTree;
using (var fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan))
{
syntaxTree = parser.Parse(fs, file.FullName);
}
trees.Add(file, syntaxTree);
var unresolvedFile = syntaxTree.ToTypeSystem();
projectContent = projectContent.AddOrUpdateFiles(unresolvedFile);
}
var compilation = projectContent.CreateCompilation();
foreach (var sharpFile in trees)
{
var originalResolver = new CSharpAstResolver(compilation, sharpFile.Value, sharpFile.Value.ToTypeSystem());
var visitor = new MissingInterfaceMemberImplementationVisitor(originalResolver);
sharpFile.Value.AcceptVisitor(visitor);
if (visitor.IsInterfaceMemberMissing)
return false;
}
return true;

Alternatively, you can also use as keyword.
class MyClass { }
interface IInterface { }
MyClass instance = new MyClass();
var inst = instance as IInterface;
if (inst != null)
{
// your class implements the interface
}
Use this when you need that instance use afterwards without additional reference typing. Using is you still need to do
if (instance is IInterface)
{
var inst = (IInterface) instance;
}
or
(instance as IInterface).InstanceProperty
so why don't do it right away.

Related

Can't access arguments of attribute from system library using source generator

I am trying to make a source generator for mapping columns from the google bigquery api client to class properties. I'm having trouble getting custom column names from a ColumnAttribute on the properties. ConstructorArguments is always empty and columnAttribute.AttributeClass in this sample is always an ErrorTypeSymbol. If I try to load that type using compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute") the result is always null.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace BigQueryMapping;
[Generator]
public class BigQueryMapperGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// add marker attribute
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedAttribute.g.cs", SourceText.From(Attribute, Encoding.UTF8)));
// add static interface
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedInterface.g.cs", SourceText.From(Interface, Encoding.UTF8)));
// get classes
IncrementalValuesProvider<ClassDeclarationSyntax> classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => s is ClassDeclarationSyntax c && c.AttributeLists.Any(),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)
)
.Where(static m => m is not null)!;
IncrementalValueProvider<(Compilation Compilation, ImmutableArray<ClassDeclarationSyntax>Syntaxes)>
compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses,
static (spc, source) => Execute(source.Compilation, source.Syntaxes, spc));
static ClassDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists)
{
foreach (var attributeSyntax in attributeListSyntax.Attributes)
{
var fullName = context.SemanticModel.GetTypeInfo(attributeSyntax).Type?.ToDisplayString();
if (fullName == "BigQueryMapping.BigQueryMappedAttribute")
return classDeclarationSyntax;
}
}
return null;
}
static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> classes,
SourceProductionContext context)
{
try
{
if (classes.IsDefaultOrEmpty)
return;
var distinctClasses = classes.Distinct();
var classesToGenerate = GetTypesToGenerate(compilation, distinctClasses, context.CancellationToken);
foreach (var classToGenerate in classesToGenerate)
{
var result = GeneratePartialClass(classToGenerate);
context.AddSource($"{classToGenerate.RowClass.Name}.g.cs", SourceText.From(result, Encoding.UTF8));
}
}
catch (Exception e)
{
var descriptor = new DiagnosticDescriptor(id: "BQD001",
title: "Error creating bigquery mapper",
messageFormat: "{0} {1}",
category: "BigQueryMapperGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
context.ReportDiagnostic(Diagnostic.Create(descriptor, null, e.Message, e.StackTrace));
}
}
}
static IEnumerable<ClassToGenerate> GetTypesToGenerate(Compilation compilation,
IEnumerable<ClassDeclarationSyntax> classes,
CancellationToken ct)
{
var columnAttributeSymbol =
compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute");
foreach (var #class in classes)
{
Debug.WriteLine($"Checking class {#class}");
ct.ThrowIfCancellationRequested();
var semanticModel = compilation.GetSemanticModel(#class.SyntaxTree);
if (semanticModel.GetDeclaredSymbol(#class) is not INamedTypeSymbol classSymbol)
continue;
var info = new ClassToGenerate(classSymbol, new());
foreach (var member in classSymbol.GetMembers())
{
if (member is IPropertySymbol propertySymbol)
{
if (propertySymbol.DeclaredAccessibility == Accessibility.Public)
{
if (propertySymbol.SetMethod is not null)
{
var columnName = propertySymbol.Name;
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
if (columnAttribute is not null)
{
if (!columnAttribute.ConstructorArguments.IsDefaultOrEmpty)
{
var nameArg = columnAttribute.ConstructorArguments.First();
if (nameArg.Value is string name)
{
columnName = name;
}
}
}
info.Properties.Add((columnName, propertySymbol));
}
}
}
}
yield return info;
}
}
static string GeneratePartialClass(ClassToGenerate c)
{
var sb = new StringBuilder();
sb.Append($#"// <auto-generated/>
namespace {c.RowClass.ContainingNamespace.ToDisplayString()}
{{
public partial class {c.RowClass.Name} : BigQueryMapping.IBigQueryGenerated<{c.RowClass.Name}>
{{
public static {c.RowClass.Name} FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row)
{{
return new {c.RowClass.Name}
{{");
foreach (var (columnName, property) in c.Properties)
{
// would like to check if key exists but don't see any sort of ContainsKey implemented on BigQueryRow
var tempName = $"___{property.Name}";
var basePropertyType = property.Type.WithNullableAnnotation(NullableAnnotation.None).ToDisplayString();
if (basePropertyType.EndsWith("?"))
{
basePropertyType = basePropertyType.Substring(default, basePropertyType.Length - 1);
}
sb.Append($#"
{property.Name} = row[""{columnName}""] is {basePropertyType} {tempName} ? {tempName} : default,");
}
sb.Append($#"
}};
}}
}}
}}");
return sb.ToString();
}
private record struct ClassToGenerate(INamedTypeSymbol RowClass,
List<(string ColumnName, IPropertySymbol Property)> Properties);
public const string Attribute = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
[System.AttributeUsage(System.AttributeTargets.Class)]
public class BigQueryMappedAttribute : System.Attribute
{
}
}";
public const string Interface = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
public interface IBigQueryGenerated<TRow> {
static TRow FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row) => throw new System.NotImplementedException();
}
}";
}
I have tried this with both System.ComponentModel.DataAnnotations.Schema.ColumnAttribute and a custom attribute injected via context.RegisterPostInitializationOutput to similar results. I have also tried rewriting this to use ISourceGenerator instead of IIncrementalGenerator and gotten the same behavior. Am wondering what I need to do to get columnAttribute loading correctly.
Thanks for any help in advance
It's hard to psychic debug the code but this does jump out:
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
I'd guess the class here would be the fully qualified name. You already fetched the ColumnAttribute type earlier, so what'd be even better is to compare the AttributeClass to that type rather than doing string checks like this.
As a semi-related comment, if you're looking for types/members that are annotated with a specific attribute, rather than doing it yourself we have SyntaxValueProvider.ForAttributeWithMetadataName which is pretty heavily optimized to reduce the performance impact on your Visual Studio. It requires 17.3 or higher, but as long as you're OK with that it'll generally help performance.

Using specific function to create new instance of object when deserializing CSV using CSVHelper

I am using JoshClose's CsvHelper library in order to serialize and deserialize data into CSV.
I am facing the following problem : When deserializing my data, I want to use a specific function to create a new instance of my class being deserialized.
I saw I can use a specific constructor as indicated here, but in my case, I would like to use a function which is not a constructor (but still returns a new instance of my class). Is this even possible?
Another solution would be to allow CSVHelper to "override" the values of an existing instance of my class. Is this possible?
Here is my current code so far:
private MyClass[] DeserializeFromCSV( string csv )
{
MyClass[] output = null;
using ( System.IO.TextReader textReader = new System.IO.StringReader( csv ) )
using ( CsvHelper.CsvReader csvReader = new CsvHelper.CsvReader( textReader ) )
{
// Here, I would like to provide the function to call
// in order to instantiate a new instance of the class
// The function I want to call does not return a `ConstructorInfo`
// as needed by the property here
//csvReader.Configuration.GetConstructor = type =>
//{
// return null;
//};
// CSVClassMap is a getter to get a ClassMap
csvReader.Configuration.RegisterClassMap( CSVClassMap );
csvReader.Read();
output = csvReader.GetRecords<T>().ToArray();
}
return output;
}
You can use CsvHelper.ObjectResolver to do this.
void Main()
{
var s = new StringBuilder();
s.Append("Id,Name\r\n");
s.Append("1,one\r\n");
s.Append("2,two\r\n");
using (var reader = new StringReader(s.ToString()))
using (var csv = new CsvReader(reader))
{
CsvHelper.ObjectResolver.Current = new ObjectResolver(CanResolve, Resolve);
csv.Configuration.RegisterClassMap<TestMap>();
csv.GetRecords<Test>().ToList().Dump();
}
}
public bool CanResolve(Type type)
{
return type == typeof(Test);
}
public object Resolve(Type type, object[] constructorArgs)
{
// Get a dependency from somewhere.
var someDependency = new object();
return new Test(someDependency);
}
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
public Test(object someDependency) { }
}
public class TestMap : ClassMap<Test>
{
public TestMap()
{
Map(m => m.Id);
Map(m => m.Name);
}
}

Automate class generation from an interface using field/property naming conventions

How would I automate the creation of a default implementation of a class from an interface using conventions. In other words, if I have an interface:
public interface ISample
{
int SampleID {get; set;}
string SampleName {get; set;}
}
Is there a snippet, T4 template, or some other means of automatically generating the class below from the interface above? As you can see, I want to put the underscore before the name of the field and then make the field the same name as the property, but lower-case the first letter:
public class Sample
{
private int _sampleID;
public int SampleID
{
get { return _sampleID;}
set { _sampleID = value; }
}
private string _sampleName;
public string SampleName
{
get { return _sampleName;}
set { _sampleName = value; }
}
}
I am not sure if T4 would be the easiest solution here in terms of readability but you can also use another code generation tool at your disposal: the CodeDom provider.
The concept is very straightforward: code consists of building blocks that you put together.
When the time is ripe, these building blocks are then parsed into the language of choice . What you end up with is a string that contains the source code of your newly created program. Afterwards you can write this to a textfile to allow for further use.
As you have noticed: there is no compile-time result, everything is runtime. If you really want compiletime then you should use T4 instead.
The code:
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Reflection;
using System.Text;
namespace TTTTTest
{
internal class Program
{
private static void Main(string[] args)
{
new Program();
}
public Program()
{
// Create namespace
var myNs = new CodeNamespace("MyNamespace");
myNs.Imports.AddRange(new[]
{
new CodeNamespaceImport("System"),
new CodeNamespaceImport("System.Text")
});
// Create class
var myClass = new CodeTypeDeclaration("MyClass")
{
TypeAttributes = TypeAttributes.Public
};
// Add properties to class
var interfaceToUse = typeof (ISample);
foreach (var prop in interfaceToUse.GetProperties())
{
ImplementProperties(ref myClass, prop);
}
// Add class to namespace
myNs.Types.Add(myClass);
Console.WriteLine(GenerateCode(myNs));
Console.ReadKey();
}
private string GenerateCode(CodeNamespace ns)
{
var options = new CodeGeneratorOptions
{
BracingStyle = "C",
IndentString = " ",
BlankLinesBetweenMembers = false
};
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
CodeDomProvider.CreateProvider("C#").GenerateCodeFromNamespace(ns, writer, options);
}
return sb.ToString();
}
private void ImplementProperties(ref CodeTypeDeclaration myClass, PropertyInfo property)
{
// Add private backing field
var backingField = new CodeMemberField(property.PropertyType, GetBackingFieldName(property.Name))
{
Attributes = MemberAttributes.Private
};
// Add new property
var newProperty = new CodeMemberProperty
{
Attributes = MemberAttributes.Public | MemberAttributes.Final,
Type = new CodeTypeReference(property.PropertyType),
Name = property.Name
};
// Get reference to backing field
var backingRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), backingField.Name);
// Add statement to getter
newProperty.GetStatements.Add(new CodeMethodReturnStatement(backingRef));
// Add statement to setter
newProperty.SetStatements.Add(
new CodeAssignStatement(
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), backingField.Name),
new CodePropertySetValueReferenceExpression()));
// Add members to class
myClass.Members.Add(backingField);
myClass.Members.Add(newProperty);
}
private string GetBackingFieldName(string name)
{
return "_" + name.Substring(0, 1).ToLower() + name.Substring(1);
}
}
internal interface ISample
{
int SampleID { get; set; }
string SampleName { get; set; }
}
}
This produces:
Magnificent, isn't it?
Sidenote: a property is given Attributes = MemberAttributes.Public | MemberAttributes.Final because omitting the MemberAttributes.Final would make it become virtual.
And last but not least: the inspiration of this awesomeness. Metaprogramming in .NET by Kevin Hazzard and Jason Bock, Manning Publications.

Adding to List inside a Using block

I have a class which implements IDisposable like such
public class SomeClass : IDisposable
{
private IList<string> _someList = new List<string>();
public IList<string> SomeList
{
get { return _someList; }
}
public void Dispose()
{
_someList = null;
}
}
and a method that uses 'using' blocks to create two instances of this class and add it to a collection like such
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
}
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
Console.ReadLine();
}
Whats happening here is by the time i come to the foreach to iterate through my elements in "classes" the "SomeList" property of both my objects are null.
I do understand that since each using executes Dispose() and in the dispose, i AM null-ing the two lists, they will be null.
My question is how do i achieve this without having to stop null-ing "SomeList" inside my dispose().
thanks
--UPDATE 1
Something closer to real code
base.OnPreRender(e);
Page page = HttpContext.Current.CurrentHandler as Page;
HtmlHead head = (HtmlHead)page.Header;
if (Settings.AreSet)
{
using (var noIndex = new HtmlMeta())
{
noIndex.Name = "somename";
noIndex.Content = "somecontent";
head.Controls.AddAt(0, noIndex);
}
}
using (var machineName = new HtmlMeta())
{
machineName.Name = "somename2";
machineName.Content = "somecontent2";
head.Controls.AddAt(1, machineName);
}
UpdateHeader(head);
--UPDATE #2
A variant of the above class with a string property now, as opposed to a a list.
public class SomeClass2 : IDisposable
{
public string SomeString { get; set; }
public void Dispose()
{
SomeString = string.Empty;
}
}
It still does the same thing for the following method. My 'classes' contains lists of 'SomeClass2' which has 'SomeString' as empty (what i set in my dispose())
IList<SomeClass2> classes = new List<SomeClass2>();
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeString);
}
Console.ReadLine();
You don't want Dispose to be called? Just don't use using statement.
Or move the entire logic inside using:
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
}
}
Console.ReadLine();
}
Btw: looks like using IDisposable in that case is pointless. GC would gather unused List<T> objects anyway.

C# XmlSerialisation addin serialisation

I have written an Interface for writing a very very simple Plugin. In fact it is just a class that is loaded at runtime out of a dll file and is stored as Property in another class. That class that stores the interface has to get serialized. As example this is my serialized object:
<?xml version="1.0" encoding="utf-8"?><MD5HashMapper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.namespace.net" />
But now If i want to load that Object I get an Exception:
As example :
{"<MD5HashMapper xmlns='http://www.vrz.net/Vrz.Map'> was not expected."}
So does anyone has an idea how to solve that problem?
Code:
I have an Interface named IMap that is shared in a dll file to create Addins based on that interface:
public interface IMap
{
object Map(object input);
}
I also have different Mappers (you can pass an input through them and they modify the output). All Mappers are derived from:
[XmlInclude(typeof(ConstMapper))]
[XmlInclude(typeof(FuncMapper))]
[XmlInclude(typeof(IdentMapper))]
[XmlInclude(typeof(NullMapper))]
[XmlInclude(typeof(RefMapper))]
[XmlInclude(typeof(VarMapper))]
[XmlInclude(typeof(TableMapper))]
[XmlInclude(typeof(AddinMapper))]
public class MapperBase:ComponentBase,IMap
{ public virtual object Map(object input) {
throw new NotImplementedException("Diese Methode muss überschrieben werden");
}
public override string ToString() {
return ShortDisplayName;
}
}
Just forget ComponentBase. It is not important for this...
Now i also have a AddinMapper. The main function of that mapper is to cast create MapperBase Object out of the IMap object:
And that is exactly that class I want to seralize including the properties of the Mapper Property (type IMap).
public class AddinMapper : MapperBase
{
private static MapperBase[] _mappers;
const string addinDirectory = #"Addin\Mappers\";
//Mappers from *.dll files are loaded here:
[XmlIgnore]
public static MapperBase[] Mappers
{
get
{
if (_mappers == null)
{
List<MapperBase> maps = new List<MapperBase>();
foreach (string dll in Directory.GetFiles(addinDirectory, "*.dll"))
{
if (Path.GetFileName(dll) != "IMap.dll")
{
var absolutePath = Path.Combine(Environment.CurrentDirectory, dll);
Assembly asm = Assembly.LoadFile(absolutePath);
foreach (Type type in asm.GetTypes().ToList().Where(p => p.GetInterface("IMap") != null))
{
maps.Add(new AddinMapper((IMap)Activator.CreateInstance(type)));
}
}
}
Mappers = maps.ToArray();
}
return _mappers;
}
set
{
_mappers = value;
}
}
IMap _base;
public string MapperString { get; set; }
[XmlIgnore()]
public IMap Mapper
{
get
{
if (_base == null)
{
Type type = null;
foreach (MapperBase mapperBase in Mappers)
{
if (mapperBase is AddinMapper && ((AddinMapper)mapperBase).Mapper.GetType().FullName == _mapperName)
{
type = (mapperBase as AddinMapper).Mapper.GetType();
break;
}
}
if (type != null)
{
XmlSerializer serializer = new XmlSerializer(type);
using (StringReader reader = new StringReader(MapperString))
{
Mapper = (IMap)serializer.Deserialize(reader);
}
}
}
return _base;
}
private set
{
_base = value;
StoreMapperString();
}
}
string _mapperName;
[System.ComponentModel.Browsable(false)]
public string MapperName
{
get
{
return _mapperName;
}
set
{
_mapperName = value;
}
}
public AddinMapper(IMap baseInterface) : this()
{
Mapper = baseInterface;
_mapperName = baseInterface.GetType().FullName;
}
public AddinMapper()
{
}
public override object Map(object input)
{
return Mapper.Map(input);
}
public override string ToString()
{
return Mapper.ToString();
}
private void StoreMapperString()
{
MemoryStream memstream = new MemoryStream();
XmlStore.SaveObject(memstream, Mapper);
using (StreamReader reader = new StreamReader(memstream))
{
memstream.Position = 0;
MapperString = reader.ReadToEnd();
}
}
}
An example for such a addin would be:
public class ReplaceMapper : IMap
{
public string StringToReplace { get; set; }
public string StringToInsert { get; set; }
public object Map(object input)
{
if (input is string)
{
input = (input as string).Replace(StringToReplace, StringToInsert);
}
return input;
}
}
And the Problem is I want to save the Settings like StringToReplace,... as xml
I ve solved my problem:
I really don t know why but take a look at this article: http://www.calvinirwin.net/2011/02/10/xmlserialization-deserialize-causes-xmlns-was-not-expected/
(if link is dead later)
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = elementName;
xRoot.IsNullable = true;
XmlSerializer ser = new XmlSerializer(typeof(MyObject), xRoot);
XmlReader xRdr = XmlReader.Create(new StringReader(xmlData));
MyObject tvd = (MyObject)ser.Deserialize(xRdr);
Now the important thing: It does not matter if you don t get an excption on serialization. You have to add the XmlRootAttribute on both ways: Serialisation and Deserialization.

Categories