Tried the code in Visual Studio on Windows to be sure.
Mono framework does not appear to honor the EmitDefaultValue argument of the DataMemberAttribute. Using the following code:
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
namespace MyApp
{
class MainClass
{
public static void Main (string[] args)
{
Cereal specialK = new Cereal();
specialK.TheValue="This is a what?";
var ser = new DataContractJsonSerializer(typeof(Cereal));
MemoryStream stm = new MemoryStream();
ser.WriteObject(stm, specialK);
string json = System.Text.Encoding.UTF8.GetString(stm.ToArray());
Console.WriteLine(json);
Console.ReadLine();
}
}
[DataContract]
class Cereal
{
[DataMember(Name="set_on_serialize")]
private string _setOnSerialize = string.Empty;
[DataMember(Name = "default_export", EmitDefaultValue = false)]
private string _default_null;
public Cereal() { }
[DataMember(Name = "out_value")]
public string TheValue
{
get;
set;
}
[OnSerializing]
void OnSerializing(StreamingContext content)
{
this._setOnSerialize = "A brick!";
}
}
}
The output in Mono results in:
{"default_export":null,"out_value":"This is a what?","set_on_serialize":""}
The default_export property is being exported as null but should not be output as it is the default value of type string.
Correct output from VS on Windows is:
{"out_value":"This is a what?","set_on_serialize":"A brick!"}
Is this a bug in Mono or am I missing something?
Apparently this feature is not yet implemented in mono. See the FIXME comment in the mono source code (line 197).
Related
How to create a C# class which must be used to deserialize the XML as given below
<?xml version="1.0" encoding="utf-8"?>
<XML>
<StatusCode>-2</StatusCode>
<Warnings />
<Errors>
<Error> Debtor #2 Invalid Postal Code</Error>
<Error>Invalid lien term</Error>
</Errors>
</XML>
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication110
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.xml";
const string OUTPUT_FILENAME = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(INPUT_FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(XML));
XML xml = (XML)serializer.Deserialize(reader);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME, settings);
serializer.Serialize(writer, xml);
}
}
public class XML
{
public int StatusCode { get; set; }
public string Warnings { get; set; }
[XmlArray("Errors")]
[XmlArrayItem("Error")]
public List<string> errors { get; set; }
}
}
To create classes based on XML, copy the xml to the clipboard, then in Visual Studio 2017, choose the menu option: Edit/Paste Special/Paste XML as classes.
Your Class should look like:
public class ErrorClass
{
struct Error
{
public String message;
}
struct Warning
{
public String message;
}
int StatusCode;
List<Error> Errors;
List<Warning> Warnings;
}
Error and Warning struct could contain more items that are not used in the example you posted.
I'm trying to compile the following code using the .NET 4.5 Framework:
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Discussion.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
class CodeReview
{
public List<CodeReviewComment> GetCodeReviewComments(int workItemId)
{
List<CodeReviewComment> comments = new List<CodeReviewComment>();
Uri tfsuri = new Uri(MYURL);
TeamFoundationDiscussionService service = new TeamFoundationDiscussionService();
service.Initialize(new Microsoft.TeamFoundation.Client.TfsTeamProjectCollection(tfsuri));
IDiscussionManager discussionManager = service.CreateDiscussionManager();
IAsyncResult result = discussionManager.BeginQueryByCodeReviewRequest(workItemId, QueryStoreOptions.ServerAndLocal, new AsyncCallback(CallCompletedCallback), null);
var output = discussionManager.EndQueryByCodeReviewRequest(result);
foreach (DiscussionThread thread in output)
{
if (thread.RootComment != null)
{
CodeReviewComment comment = new CodeReviewComment();
comment.Author = thread.RootComment.Author.DisplayName;
comment.Comment = thread.RootComment.Content;
comment.PublishDate = thread.RootComment.PublishedDate.ToShortDateString();
comment.ItemName = thread.ItemPath;
comments.Add(comment);
}
}
return comments;
}
static void CallCompletedCallback(IAsyncResult result)
{
// Handle error conditions here
}
public class CodeReviewComment
{
public string Author { get; set; }
public string Comment { get; set; }
public string PublishDate { get; set; }
public string ItemName { get; set; }
}
}
The Visual Studio compiler is complaining that "the type or namespace 'Uri' could not be found" and when I expand the System namespace in the Object Browser it doesnt list any of the Uri classes. I've tried a few other versions of .NET and still have the same problem although it does appear if I select the .NET 4.0 Client Profile but then none of the TeamFoundation classes are present in the Microsoft namespace.
I solved my own problem but I don't understand why it worked. In desperation I created another new Project using .NET 4.5 and copied and pasted the code over. It compiled and Uri shows up as a Class in the Object Browser. I have no idea what the difference between the 2 projects is.
Is it possible to include a web.config or app.config file in the azure functions folder structure to allow assembly binding redirects?
Assuming you are using the latest (June'17) Visual Studio 2017 Function Tooling, I derived a somewhat-reasonable config-based solution for this following a snippet of code posted by npiasecki over on Issue #992.
It would be ideal if this were managed through the framework, but at least being configuration-driven you have a bit more change isolation. I suppose you could also use some pre-build steps or T4 templating that reconciles the versions of the nugets in the project (and their dependencies) before writing out this config or generating code.
So the downside..
.. becomes having to remember to update the BindingRedirects config when you update the NuGet package (this is often a problem in app.configs anyway). You may also have an issue with the config-driven solution if you need to redirect Newtonsoft.
In our case, we were using the new Azure Fluent NuGet that had a dependency on an older version of Microsoft.IdentityModel.Clients.ActiveDirectory than the version of the normal ARM management libraries which are used side-by-side in a particular Function.
local.settings.json
{
"IsEncrypted": false,
"Values": {
"BindingRedirects": "[ { \"ShortName\": \"Microsoft.IdentityModel.Clients.ActiveDirectory\", \"RedirectToVersion\": \"3.13.9.1126\", \"PublicKeyToken\": \"31bf3856ad364e35\" } ]"
}
}
FunctionUtilities.cs
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace Utilities.AzureFunctions
{
public static class FunctionUtilities
{
public class BindingRedirect
{
public string ShortName { get; set; }
public string PublicKeyToken { get; set; }
public string RedirectToVersion { get; set; }
}
public static void ConfigureBindingRedirects()
{
var config = Environment.GetEnvironmentVariable("BindingRedirects");
var redirects = JsonConvert.DeserializeObject<List<BindingRedirect>>(config);
redirects.ForEach(RedirectAssembly);
}
public static void RedirectAssembly(BindingRedirect bindingRedirect)
{
ResolveEventHandler handler = null;
handler = (sender, args) =>
{
var requestedAssembly = new AssemblyName(args.Name);
if (requestedAssembly.Name != bindingRedirect.ShortName)
{
return null;
}
var targetPublicKeyToken = new AssemblyName("x, PublicKeyToken=" + bindingRedirect.PublicKeyToken)
.GetPublicKeyToken();
requestedAssembly.Version = new Version(bindingRedirect.RedirectToVersion);
requestedAssembly.SetPublicKeyToken(targetPublicKeyToken);
requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;
AppDomain.CurrentDomain.AssemblyResolve -= handler;
return Assembly.Load(requestedAssembly);
};
AppDomain.CurrentDomain.AssemblyResolve += handler;
}
}
}
Just posted a new blog post explaining how to fix the problem, have a look:
https://codopia.wordpress.com/2017/07/21/how-to-fix-the-assembly-binding-redirect-problem-in-azure-functions/
It's actually a tweaked version of the JoeBrockhaus's code, that works well even for Newtonsoft.Json.dll
Inspired by the accepted answer I figured I'd do a more generic one which takes into account upgrades as well.
It fetches all assemblies, orders them descending to get the newest version on top, then returns the newest version on resolve. I call this in a static constructor myself.
public static void RedirectAssembly()
{
var list = AppDomain.CurrentDomain.GetAssemblies()
.Select(a => a.GetName())
.OrderByDescending(a => a.Name)
.ThenByDescending(a => a.Version)
.Select(a => a.FullName)
.ToList();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var requestedAssembly = new AssemblyName(args.Name);
foreach (string asmName in list)
{
if (asmName.StartsWith(requestedAssembly.Name + ","))
{
return Assembly.Load(asmName);
}
}
return null;
};
}
It is not directly possible today, but we are thinking about ways to achieve this. Can you please open an issue on https://github.com/Azure/azure-webjobs-sdk-script/issues to make sure your specific scenario is looked at? Thanks!
First SO post, so apologies if formatting's a bit off.
We've hit this issue a couple of times and managed to find a better way of getting the required redirects by forcing MSBUILD to generate a binding redirects file and then parsing that to be used with the previously suggested answer.
Modify the project settings and add in a couple of targets:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
...
<AutoGenerateBindingRedirects>True</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
...
</PropertyGroup>
</Project>
These classes apply the binding redirects using the same idea that was posted earlier (link) except instead of using the host.json file it reads from the generated binding redirects file. The filename to use is from reflection using the ExecutingAssembly.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
public static class AssemblyBindingRedirectHelper
{
private static FunctionRedirectBindings _redirects;
public static void ConfigureBindingRedirects()
{
// Only load the binding redirects once
if (_redirects != null)
return;
_redirects = new FunctionRedirectBindings();
foreach (var redirect in _redirects.BindingRedirects)
{
RedirectAssembly(redirect);
}
}
public static void RedirectAssembly(BindingRedirect bindingRedirect)
{
ResolveEventHandler handler = null;
handler = (sender, args) =>
{
var requestedAssembly = new AssemblyName(args.Name);
if (requestedAssembly.Name != bindingRedirect.ShortName)
{
return null;
}
var targetPublicKeyToken = new AssemblyName("x, PublicKeyToken=" + bindingRedirect.PublicKeyToken).GetPublicKeyToken();
requestedAssembly.Version = new Version(bindingRedirect.RedirectToVersion);
requestedAssembly.SetPublicKeyToken(targetPublicKeyToken);
requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;
AppDomain.CurrentDomain.AssemblyResolve -= handler;
return Assembly.Load(requestedAssembly);
};
AppDomain.CurrentDomain.AssemblyResolve += handler;
}
}
public class FunctionRedirectBindings
{
public HashSet<BindingRedirect> BindingRedirects { get; } = new HashSet<BindingRedirect>();
public FunctionRedirectBindings()
{
var assm = Assembly.GetExecutingAssembly();
var bindingRedirectFileName = $"{assm.GetName().Name}.dll.config";
var dir = Path.Combine(Environment.GetEnvironmentVariable("HOME"), #"site\wwwroot");
var fullPath = Path.Combine(dir, bindingRedirectFileName);
if(!File.Exists(fullPath))
throw new ArgumentException($"Could not find binding redirect file. Path:{fullPath}");
var xml = ReadFile<configuration>(fullPath);
TransformData(xml);
}
private T ReadFile<T>(string path)
{
using (StreamReader reader = new StreamReader(path))
{
var serializer = new XmlSerializer(typeof(T));
var obj = (T)serializer.Deserialize(reader);
reader.Close();
return obj;
}
}
private void TransformData(configuration xml)
{
foreach(var item in xml.runtime)
{
var br = new BindingRedirect
{
ShortName = item.dependentAssembly.assemblyIdentity.name,
PublicKeyToken = item.dependentAssembly.assemblyIdentity.publicKeyToken,
RedirectToVersion = item.dependentAssembly.bindingRedirect.newVersion
};
BindingRedirects.Add(br);
}
}
}
public class BindingRedirect
{
public string ShortName { get; set; }
public string PublicKeyToken { get; set; }
public string RedirectToVersion { get; set; }
}
Xml classes to use to deserialise the generated binding redirect file into something easier to use. These were generated from the binding redirects file by using VS2017 "paste special -> paste xml as classes" so feel free to roll your own if needed.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
// NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class configuration
{
[System.Xml.Serialization.XmlArrayItemAttribute("assemblyBinding", Namespace = "urn:schemas-microsoft-com:asm.v1", IsNullable = false)]
public assemblyBinding[] runtime { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-microsoft-com:asm.v1")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "urn:schemas-microsoft-com:asm.v1", IsNullable = false)]
public partial class assemblyBinding
{
public assemblyBindingDependentAssembly dependentAssembly { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-microsoft-com:asm.v1")]
public partial class assemblyBindingDependentAssembly
{
public assemblyBindingDependentAssemblyAssemblyIdentity assemblyIdentity { get; set; }
public assemblyBindingDependentAssemblyBindingRedirect bindingRedirect { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-microsoft-com:asm.v1")]
public partial class assemblyBindingDependentAssemblyAssemblyIdentity
{
[System.Xml.Serialization.XmlAttributeAttribute()]
public string name { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string publicKeyToken { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string culture { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-microsoft-com:asm.v1")]
public partial class assemblyBindingDependentAssemblyBindingRedirect
{
[System.Xml.Serialization.XmlAttributeAttribute()]
public string oldVersion { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string newVersion { get; set; }
}
Here's an alternate solution for when you want the exact version of a particular assembly. With this code, you can easily deploy the assemblies that are missing:
public static class AssemblyHelper
{
//--------------------------------------------------------------------------------
/// <summary>
/// Redirection hack because Azure functions don't support it.
/// How to use:
/// If you get an error that a certain version of a dll can't be found:
/// 1) deploy that particular dll in any project subfolder
/// 2) In your azure function static constructor, Call
/// AssemblyHelper.IncludeSupplementalDllsWhenBinding()
///
/// This will hook the binding calls and look for a matching dll anywhere
/// in the $HOME folder tree.
/// </summary>
//--------------------------------------------------------------------------------
public static void IncludeSupplementalDllsWhenBinding()
{
var searching = false;
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
// This prevents a stack overflow
if(searching) return null;
var requestedAssembly = new AssemblyName(args.Name);
searching = true;
Assembly foundAssembly = null;
try
{
foundAssembly = Assembly.Load(requestedAssembly);
}
catch(Exception e)
{
Debug.WriteLine($"Could not load assembly: {args.Name} because {e.Message}");
}
searching = false;
if(foundAssembly == null)
{
var home = Environment.GetEnvironmentVariable("HOME") ?? ".";
var possibleFiles = Directory.GetFiles(home, requestedAssembly.Name + ".dll", SearchOption.AllDirectories);
foreach (var file in possibleFiles)
{
var possibleAssembly = AssemblyName.GetAssemblyName(file);
if (possibleAssembly.Version == requestedAssembly.Version)
{
foundAssembly = Assembly.Load(possibleAssembly);
break;
}
}
}
return foundAssembly;
};
}
}
In order to generate UBL-order documents in XML, I have created 44 classes in C# using Xml.Serialization. The class consist of a root class "OrderType" which contains a lot of properties (classes), which again contains more properties.
In order to test that the classes always will build a XML document that will pass a validation. I want a XML file containing all the possible nodes (at least once) the hierarchy of Classes/Properties can build.
A very reduced code example:
[XmlRootAttribute]
[XmlTypeAttribute]
public class OrderType
{
public DeliveryType Delivery { get; set; }
//+ 50 more properties
public OrderType(){}
}
[XmlTypeAttribute]
public class DeliveryType
{
public QuantityType Quantity { get; set; }
//+ 10 more properties
public DeliveryType (){}
}
I have already tried to initialise some properties in some of the constructors and it works fine, but this method would take a whole week to finish.
So! Is there a smart an quick method to generate a Mock XML document with all properties initialized?
It's ok that the outer nodes just are defined e.g.:
< Code />
Well, sometimes you have to do it on your own:
using System;
using System.IO;
using System.Xml.Serialization;
using System.Reflection;
namespace extanfjTest
{
class Program
{
static void Main(string[] args)
{
OrderType ouo = new OrderType(DateTime.Now);
SetProperty(ouo);
XmlSerializer ser = new XmlSerializer(typeof(OrderType));
FileStream fs = new FileStream("C:/Projects/group.xml", FileMode.Create);
ser.Serialize(fs, ouo);
fs.Close();
}
public static void SetProperty(object _object)
{
if (_object == null)
{ return; }
foreach (PropertyInfo prop in _object.GetType().GetProperties())
{
if ("SemlerServices.OIOUBL.dll" != prop.PropertyType.Module.Name)
{ continue; }
if (prop.PropertyType.IsArray)
{
var instance = Activator.CreateInstance(prop.PropertyType.GetElementType());
Array _array = Array.CreateInstance(prop.PropertyType.GetElementType(), 1);
_array.SetValue(instance, 0);
prop.SetValue(_object, _array, null);
SetProperty(instance);
}
else
{
var instance = Activator.CreateInstance(prop.PropertyType);
prop.SetValue(_object, instance, null);
SetProperty(instance);
}
}
}
}
}
I have an XML document I'm trying to deseralize that has a attribute that is ref which in C# can not be used to declare a variable hence the below doesn't work
[XmlAttribute()]
public string ref;
Anyway to get this to deseralize properly? I know it is case sensitive so Ref wouldn't work.
You can provide a name in the attribute:
[XmlAttribute("ref")]
public string anynameyouwant;
You can change the attribute name in the xml file, by using the AttributeName, such as in the following example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace soans
{
public class Test
{
//problematic attribute (ref is reserved)
[XmlAttribute(AttributeName="ref")]
public string RefAttr {get;set;}
//other attributes as well
[XmlAttribute()]
public string Field { get; set; }
}
class Program
{
static void Main(string[] args)
{
string filename = ""; //use your path here
Test original = new Test()
{
RefAttr = "ref",
Field = "test"
};
//serialiser
XmlSerializer ser = new XmlSerializer(typeof(Test));
//save to file
TextWriter writer = new StreamWriter(filename);
ser.Serialize(writer, original);
writer.Close();
//read from file
TextReader reader = new StreamReader(filename);
var fromfile = ser.Deserialize(reader) as Test;
if(fromfile!=null)
{
Console.WriteLine(fromfile.RefAttr);
}
reader.Close();
Console.ReadKey();
}
}
}