I have a serializable class in some library assembly.
also I have to projects referencing this assembly.
One project serializes an instance of my class,
other project serializes this instance of my class.
I copied dll of this assembly into some backup folder to get ability to restore an old version.
I added new field in my class.
I serialized an instance of my updated class.
Then I restored old version of my class and deserialized the instance.
BinaryFormatter works well and doesn't throw an exception.
Do we need an [optionalField] attribute?
Library
using System;
namespace SerializationFramework
{
[Serializable]
public class MyClass
{
public string Field { get; set; }
//public string NewField { get; set; }//added in Version 2, without [OptionalField] attribute
public MyClass()
{
Field = "test";
//NewField = "newField";
}
//for checking versions
public void Test()
{
Console.WriteLine("Version 1");//comment after adding a new field
//Console.WriteLine("Version 2");//uncomment after adding a new field
Console.ReadLine();
}
}
}
Serializing
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using SerializationFramework;
namespace Serialization
{
class Program
{
static void Main(string[] args)
{
var binaryF = new BinaryFormatter();
var obj = new MyClass();
using (var f = File.Create(#"C:\temp\oleg.txt"))
{
binaryF.Serialize(f, obj);
}
Console.WriteLine("Serialized!");
Console.ReadLine();
}
}
}
Deserializing
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using SerializationFramework;
namespace Deserialization
{
class Program
{
static void Main(string[] args)
{
var binaryF = new BinaryFormatter();
MyClass obj;
using (var f = File.Open(#"c:\temp\oleg.txt", FileMode.Open))
{
obj = (MyClass) binaryF.Deserialize(f);
}
//CheckingVersion
var obj2 = new MyClass();
obj2.Test();
}
}
}
Related
Let's say I have a library with the following enum attributes:
public enum LibraryAuth
{
Online,
Offline,
}
This enum is being used in an Object of the library.
Now I have my own enum which goes like this:
[Serializable, XmlType]
public enum MyAuth
{
[XmlEnum]
Online,
[XmlEnum]
Offline,
}
This one is supposed to be used within a class of my own programm:
[Serializable, DebuggerStepThrough, DesignerCategory("code"), GeneratedCode("WebServiceProxyUpdater", "1.0.0.0"), XmlType]
public class MyClass
{
[XmlAttribute]
public MyAuth auth { get; set; }
}
The object of the library and my object have the same attributes.
Now I am trying to cast the object of the library into the object of my program with this function:
public static destT CreateCopy<sourceT, destT>(sourceT sourceObj)
{
try
{
XmlSerializer sourceXS = new XmlSerializer(typeof(sourceT));
XmlSerializer destXS = new XmlSerializer(typeof(destT));
using (MemoryStream ms = new MemoryStream())
{
sourceXS.Serialize(ms, sourceObj);
ms.Position = 0;
return (destT)destXS.Deserialize(ms);
}
}
catch (Exception ex)
{
return default(destT);
}
}
Unfortunately this throws the following error: Instance validation error: '' is not a valid value for MyAuth
If I replace my enum with the enum of the library the serialisation works just fine. But somehow serializing it with my enum does not work.
What am I missing in my Enum Class?
EDIT: I've read about a similar problem in this thread but the OP in that thread wanted to ignore the error. I on the other hand want to cast one object into another.
Locally, this works just fine and does not show the error you report. My changes:
made MyClass be public
added a LibraryClass that mimics the source type; note in particular the use of [XmlRoot]
using System;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.Serialization;
static class P
{
static void Main()
{
var obj = new LibraryClass { auth = LibraryAuth.Offline };
var foo = CreateCopy<LibraryClass, MyClass>(obj);
Console.WriteLine(foo.auth);
}
public static destT CreateCopy<sourceT, destT>(sourceT sourceObj)
{
XmlSerializer sourceXS = new XmlSerializer(typeof(sourceT));
XmlSerializer destXS = new XmlSerializer(typeof(destT));
using (MemoryStream ms = new MemoryStream())
{
sourceXS.Serialize(ms, sourceObj);
ms.Position = 0;
return (destT)destXS.Deserialize(ms);
}
}
}
public enum LibraryAuth
{
Online,
Offline,
}
[XmlRoot("MyClass")] // the two types must agree on naming
public class LibraryClass
{
[XmlAttribute]
public LibraryAuth auth { get; set; }
}
[Serializable, XmlType]
public enum MyAuth
{
[XmlEnum]
Online,
[XmlEnum]
Offline,
}
[Serializable, DebuggerStepThrough, DesignerCategory("code"), GeneratedCode("WebServiceProxyUpdater", "1.0.0.0"), XmlType]
public class MyClass
{
[XmlAttribute]
public MyAuth auth { get; set; }
}
I created Person.dll and register it(regsvcs.exe) in Command Promt for Visual Studio 2019. As a result of registration, I got Person.tlb. I tried to add Person.tlb in console project as reference COM component but I got warning MSB3290.
warning MSB3290: Failed to create the wrapper assembly for type
library "{8b1098cb-d453-4dc7-96ac-52df54d0a2ce}". Type library
'Person' was exported from a CLR assembly and cannot be re-imported as
a CLR assembly.
How I can to add Person.tlb in console project using reflection?
Person.dll:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
namespace COM
{
[ClassInterface(ClassInterfaceType.None)]
public class Person : ServicedComponent, COM.IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsMale { get; set; }
public void Persist(string FilePath)
{
StreamWriter oFile = new StreamWriter(FilePath);
XmlSerializer oXmlSerializer = new XmlSerializer(typeof(Person));
oXmlSerializer.Serialize(oFile, this);
oFile.Flush();
oFile.Close();
}
static public Person Retrieve(string FilePath)
{
StreamReader oFile = new StreamReader(FilePath);
XmlSerializer oXmlSerilizer = new XmlSerializer(typeof(Person));
Person oPerson = oXmlSerilizer.Deserialize(oFile) as Person;
return oPerson;
}
}
}
Console project:
using System;
namespace Test10
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
COM.Person per = new COM.Person();
per.FirstName = "Maxim";
per.LastName = "Donax";
per.Persist(#" C:\myFile.xml ");
}
}
}
I used other way: created Person.dll in Visual Studio and registerd it(regsvcs.exe). After use reference Person.tlb in Visual Basic 6.0.
EDIT: My attempt to create a simple sample was a failure. I thought I had replicated the issue with my real code, but it was really just a value not set to public, so my serialization was failing.
Original Post:
I have a class with a value to be set on it (A below.) Another class has an A in it and a setter for A's value (B below.) I have a bunch of B's in a list and a series of foreach's and casts. On the inside of it all, I call the setter, but the result of all of this is a list where the setter appears to have never been called. What's going wrong here and how do I get the new value to take?
I was able to recreate the problem in a more simple set of code.
Here's a fiddle: https://dotnetfiddle.net/B4YVHU
Alternatively, here's the code:
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace ConsoleApplication28
{
class A
{
public string value = null;
}
class B
{
private A a = new A();
public void SetAValue(string s)
{
Console.WriteLine($"Value is set to: {s}");
a.value = s;
}
}
public class Program
{
public static void Main(string[] args)
{
object list = new List<B> { new B(), new B() };
if ( list is IEnumerable<object> enumerable)
{
foreach (var value in enumerable)
{
if (value is B a)
{
a.SetAValue("blooop");
}
}
}
Console.WriteLine(Serialization.SerializeJson(list));
}
}
// You can ignore this. It's just to more easily display the object result
class Serialization
{
private static JsonSerializer _serializer;
static Serialization()
{
_serializer = new JsonSerializer();
}
public static string SerializeJson<T>( T value )
{
string jsonString;
using ( var stringWriter = new StringWriter() )
using ( var writer = new JsonTextWriter( stringWriter ) )
{
_serializer.Serialize(writer, value);
jsonString = stringWriter.ToString();
}
return jsonString;
}
}
}
in Class B:
public A a {get; set;} = new A();
In order to serialize, variables must be public.
https://dotnetfiddle.net/B4YVHU
I am trying to add support for System.Web.Mvc.HtmlHelper to a CLI app for compiling Razor templates, but although it compiles it fails at runtime with:
System.TypeLoadException: Could not load type 'HtmlHelper`1' from assembly '/Users/oligofren/src/razor-cli/build/System.Web.Mvc.dll'.
How should I proceed in fixing this?
I am not well versed in the core of .NET (here in Mono version), so I can't say if I have done anything wrong here. I have added all the assemblies to the build folder (where the exe ends up) and I also try to manually load the required assemblies before RazorEngine tries to compile the assemblies.
How can I resolve this?
Full source code
// See also tips on building cli apps with razorengine: https://github.com/Antaris/RazorEngine/blob/master/src/source/RazorEngine.Hosts.Console/RazorEngine.Hosts.Console.csproj
using System;
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;
using Moq;
using System.IO;
using Newtonsoft.Json.Linq;
using RazorEngine;
using RazorEngine.Templating; // For extension methods.
using RazorEngine.Configuration;
using RazorEngine.Text;
public class RazorCli
{
static public void Main (string[] args)
{
CheckCommandLine(args);
string template = ReadFile(args[0]);
JObject model = ParseModel(args[1]);
// try to load the required assemblies
//http://stackoverflow.com/a/23496144/200987
System.Reflection.Assembly.Load("System.Web");
System.Reflection.Assembly.Load("System.Web.Mvc");
var result = CompileTemplate(template, model);
Console.WriteLine (result);
}
private static string CompileTemplate (string template, JObject model)
{
string res = "";
var config = new TemplateServiceConfiguration();
// You can use the #inherits directive instead (this is the fallback if no #inherits is found).
config.BaseTemplateType = typeof(MyClassImplementingTemplateBase<>);
try
{
using (var service = RazorEngineService.Create(config))
{
res = service.RunCompile(template, "templateKey", null, model);
}
}
catch( RazorEngine.Templating.TemplateCompilationException ex )
{
Console.WriteLine (ex);
System.Environment.Exit(1);
}
return res;
}
/* Cannot dispatch a dynamic object to extension methods */
private static JObject ParseModel(string fileName){
string json = ReadFile(fileName);
return JObject.Parse(json);
}
private static void CheckCommandLine(string[] args){
if(args.Length != 2){
Usage();
System.Environment.Exit(1);
}
}
private static void Usage(){
string usage = "Usage: razor-cli <partial.cshtml> <model.json>\n";
Console.WriteLine(usage);
}
private static String ReadFile(string filename)
{
string result;
using (StreamReader sr = new StreamReader(filename))
{
result = sr.ReadToEnd();
}
return result;
}
}
public class MyHtmlHelper
{
public IEncodedString Raw(string rawString)
{
return new RawString(rawString);
}
}
// https://antaris.github.io/RazorEngine/TemplateBasics.html
public abstract class MyClassImplementingTemplateBase<T> : TemplateBase<T>
{
public MyClassImplementingTemplateBase()
{
Html = MvcHelpers.CreateHtmlHelper<Object>();
}
public HtmlHelper Html { get; set; }
}
// Ripped straight from a SO Q/A
// http://stackoverflow.com/questions/17271688/mocking-viewcontext-to-test-validation-error-messages
public class MvcHelpers {
public static HtmlHelper<TModel> CreateHtmlHelper<TModel>(ViewDataDictionary dictionary = null)
{
if (dictionary == null)
dictionary = new ViewDataDictionary { TemplateInfo = new TemplateInfo() };
var mockViewContext = new Mock<ViewContext>(
new ControllerContext(
new Mock<HttpContextBase>().Object,
new RouteData(),
new Mock<ControllerBase>().Object),
new Mock<IView>().Object,
dictionary,
new TempDataDictionary(),
new Mock<TextWriter>().Object);
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.Setup(v => v.ViewData).Returns(dictionary);
return new HtmlHelper<TModel>(mockViewContext.Object, mockViewDataContainer.Object);
}
}
Details on how I run this can be seen in the Makefile, if that helps.
Further details
Installed Mono 4.2.2.0 using Homebrew on OS X 10.11.4.
This has been driving me mad for a while now. I have a class which contains other classes. I need to loop through the first class looking for typeof second class then retreive the value of the fields.
the below code obviously fails on the line
Console.WriteLine(field.GetValue(mFC.field.secondClassString));
as this isn't a valid field. Possibly I'm going about this the wrong way - any ideas?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
MyFirstClass mFC = new MyFirstClass();
FieldInfo[] fI = mFC.GetType().GetFields();
foreach (FieldInfo field in fI)
{
if (field.FieldType.Name == "MySecondClass")
{
//get the fields
Console.WriteLine(field.GetValue(mFC.field.secondClassString));
}
}
}
}
class MyFirstClass
{
public MySecondClass firstMSC = new MySecondClass("First Instance");
public MySecondClass secondMSC = new MySecondClass("Second Instance");
public string firstClassString = "I'm from the first class";
public int firstClassInt = 5;
}
class MySecondClass
{
public MySecondClass(string input)
{
this.secondClassString = input;
}
public string secondClassString;
public int secondClassInt = 10;
}
}
field.GetValue accepts the instance from which it gets the field value.
In your case I would expect it should be field.GetValue(mFC).
Also field.FieldType.Name == "MySecondClass" is not the best way to check the type as type name change will cause code to break. I recommend replacing it with field.FieldType == typeof(MySecondClass).
((MySecondClass)field.GetValue(mFC)).secondClassString;
use this inside console.writeline