I am serializing and deserializing an Object in C# for Windows 8 Apps.
I am serializing my Object before passing it to the next View, because passing an object throws out exceptions.
function OnNavigatedTo:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string XMLString = e.Parameter.ToString();
var thisChannel = XmlDeserializeFromString(XMLString, typeof(Channel));
....}
Deserializing Function:
public static Channel XmlDeserializeFromString<Channel>(string objectData)
{
return (Channel)XmlDeserializeFromString(objectData, typeof(Channel));
}
public static object XmlDeserializeFromString(string objectData, Type type)
{
var serializer = new XmlSerializer(type);
object result;
using (TextReader reader = new StringReader(objectData))
{
result = serializer.Deserialize(reader);
}
return result;
}
I want to access the data in this Object, but something like: thisChannel.Name doesn't work. And I don't know how that I can continue working with this Object.
Start by dropping var in this line:
//var thisChannel = XmlDeserializeFromString(XMLString, typeof(Channel));
Channel thisChannel = XmlDeserializeFromString(XMLString, typeof(Channel));
and then you will at least get an error when the wrong object XmlDeserializeFromString() is selected.
And to be sure you use the right one:
Channel thisChannel = XmlDeserializeFromString<Channel>(XMLString);
Overloading should be used with care and generally not mixed with Type parameters.
XmlDeserializeFromString returns an object, which does not have a Name property. You need to either:
cast it to the type you want to use it as
use the generic method you added which does that:
var thisChannel = XmlDeserializeFromString<Channel>(XMLString);`
use dynamic to resolve the method name at runtime
use reflection to find the Name property at runtime
Yes JSON > XML, though you want to stick to XML, use TCD.Serialization, it offers serialization and deserialization XML and JSON to/from stream & string.
.
Don't do this.
Passing non-primitive types through a navigation parameter will cause your application to crash when it restores from Suspend.
Only ever pass a primitive type as a navigation parameter in a Windows 8 app.
See SuspensionManager Error when application has more than 1 page in windows 8 XAML/C# app
Related
Is there something like XmlRootAttribute that can be used with System.Text.JsonSerializer?
I need to be able to download data from this vendor using both XML an JSON. See sample data here:
{
"categories": [
{
"id": 125,
"name": "Trade Balance",
"parent_id": 13
}
]
}
Note the data elements are wrapped by an array named categories. When downloading using XML I can set the root element to categories and the correct object is returned (see XMLClient below ). When using JSONClient however I cannot (or do not know how) to set the root element. Best workaround I could find is to use JsonDocument which creates a string allocation. I could also create some wrapper classes for the JSON implementation but that is a lot of work that involves not only creating additional DTOs but also requires overwriting many methods on BaseClient. I also don't want to write converters - it pretty much defeats the purpose of using well-known serialization protocols. (Different responses will have a different root wrapper property name. I know the name in runtime, but not necessarily at compile time.)
public class JSONClient : BaseClient
{
protected override async Task<T> Parse<T>(string uri, string root)
{
uri = uri + (uri.Contains("?") ? "&" : "?") + "file_type=json";
var document = JsonDocument.Parse((await Download(uri)), new JsonDocumentOptions { AllowTrailingCommas = true });
string json = document.RootElement.GetProperty(root).GetRawText(); // string allocation
return JsonSerializer.Deserialize<T>(json);
}
}
public class XMLClient : BaseClient
{
protected override async Task<T> Parse<T>(string uri, string root)
{
return (T)new XmlSerializer(typeof(T), new XmlRootAttribute(root)).Deserialize(await Download(uri)); // want to do this - using JsonSerializer
}
}
public abstract class BaseClient
{
protected virtual async Task<Stream> Download(string uri)
{
uri = uri + (uri.Contains("?") ? "&" : "?") + "api_key=" + "xxxxx";
var response = await new HttpClient() { BaseAddress = new Uri(uri) }.GetAsync(uri);
return await response.Content.ReadAsStreamAsync();
}
protected abstract Task<T> Parse<T>(string uri, string root) where T : class, new();
public async Task<Category> GetCategory(string categoryID)
{
string uri = "https://api.stlouisfed.org/fred/category?category_id=" + categoryID;
return (await Parse<List<Category>>(uri, "categories"))?.FirstOrDefault();
}
}
JSON has no concept of a root element (or element names in general), so there's no equivalent to XmlRootAttribute in System.Text.Json (or Json.NET for that matter). Rather, it has the following two types of container, along with several atomic value types:
Objects, which are unordered sets of name/value pairs. An object begins with {left brace and ends with }right brace.
Arrays, which are ordered collections of values. An array begins with [left bracket and ends with ]right bracket.
As System.Text.Json.JsonSerializer is designed to map c# objects to JSON objects and c# collections to JSON arrays in a 1-1 manner, there's no built-in attribute or declarative option to tell the serializer to automatically descend the JSON hierarchy until a property with a specific name is encountered, then deserialize its value to a required type.
If you need to access some JSON data that is consistently embedded in some wrapper object containing a single property whose name is known at runtime but not compile time, i.e.:
{
"someRuntimeKnownWrapperPropertyName" : // The value you actually want
}
Then the easiest way to do that would be to deserialize to a Dictionary<string, T> where the type T corresponds to the type of the expected value, e.g.:
protected override async Task<T> Parse<T>(string uri, string root)
{
uri = uri + (uri.Contains("?") ? "&" : "?") + "file_type=json";
using var stream = await Download(uri); // Dispose here or not? What about disposing of the containing HttpResponseMessage?
var options = new JsonSerializerOptions
{
AllowTrailingCommas = true,
// PropertyNameCaseInsensitive = false, Uncomment if you need case insensitivity.
};
var dictionary = await JsonSerializer.DeserializeAsync<Dictionary<string, T>>(stream, options);
// Throw an exception if the dictionary does not have exactly one entry, with the required name
var pair = dictionary?.Single();
if (pair == null || !pair.Value.Key.Equals(root, StringComparison.Ordinal)) //StringComparison.OrdinalIgnoreCase if required
throw new JsonException();
// And now return the value
return pair.Value.Value;
}
Notes:
By deserializing to a typed dictionary rather than a JsonDocument you avoid the intermediate allocations required for the JsonDocument itself as well as the string returned by GetRawText(). The only memory overhead is for the dictionary itself.
Note also that JsonDocument is disposable. Failure to dispose it will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.
Alternatively, you could create a [custom JsonConverter](Failure to properly dispose this object will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.) to read through the incoming JSON until a property of the required name is encountered, then deserialize the value to the expected type.
For some examples, see Is there a simple way to manually serialize/deserialize child objects in a custom converter in System.Text.Json? or https://stackoverflow.com/a/62155881/3744182.
Rather than allocating an HttpClient for every call, you should allocate only one and reuse it. See Do HttpClient and HttpClientHandler have to be disposed between requests?.
You are not disposing of the HttpResponseMessage or response content stream. You may want to refactor your code to do that; see When or if to Dispose HttpResponseMessage when calling ReadAsStreamAsync?. You may also want to check response.IsSuccessStatusCode before deserializing.
In .Net 5, HttpClientJsonExtensions can make deserializing JSON returned by HttpClient simpler.
For some reason Coldfusion is having an issue with accessing the parent methods of one of the objects it creates.
consider this code:
<cfscript>
variables.sHTML = '<html><head><title></title></head><body><p>Hello <strong>World</strong></p></body></html>';
try{
variables.sAltChunkID = "altChunk1";
variables.sExportDirectory = application.sSecureExportPath&'int'&'\word\';
variables.sDLLPath = 'C:\Program Files (x86)\Open XML SDK\V2.0\lib\DocumentFormat.OpenXml.dll';
variables.sFileName = "testI.docx";
variables.sFileToWrite = variables.sExportDirectory&'#variables.sFileName#';
variables.enumWordProcessingDocumentType = createObject("dotnet","DocumentFormat.OpenXml.WordprocessingDocumentType","#variables.sDLLPath#").init().Document;
variables.oDocument = createObject("dotnet","DocumentFormat.OpenXml.Packaging.WordprocessingDocument","#variables.sDLLPath#").Create(variables.sFileToWrite,variables.enumWordProcessingDocumentType);
variables.oMainDocument = variables.oDocument.AddMainDocumentPart();
variables.oEncoding = createObject("dotnet","System.Text.UTF8Encoding").init();
//variables.oMemoryStream = createObject("dotnet","System.IO.MemoryStream").init(variables.oEncoding.GetBytes(variables.sHTML));
variables.enumAltChunk = createObject("dotnet","DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType","#variables.sDLLPath#").html;
variables.oFormatImportPart = variables.oMainDocument.AddAlternativeFormatImportPart(variables.enumAltChunk,variables.sAltChunkID);
writeDump(variables.oFormatImportPart);
variables.oFormatImportPart.FeedData(createObject("dotnet","System.IO.MemoryStream").init(variables.oEncoding.GetBytes(variables.sHTML)));
} catch(Any e) {
writeDump(e);
}
</cfscript>
variables.oFormatImportPart has a parent method of FeedData(System.IO.Stream), however when I get to that line, Coldfusion hits me with an exception of:
Either there are no methods with the specified method name and argument types or the FeedData method is overloaded with argument types that ColdFusion cannot decipher reliably. ColdFusion found 0 methods that match the provided arguments. If this is a Java object and you verified that the method exists, use the javacast function to reduce ambiguity.
But as you can see from my Dump, FeedData does indeed exist as a method:
FeedData is overloaded and expecting a Stream object. You are currently sending it an ambiguous object as it has not be cast to the proper type after your createObject call.
ok, so in javascript, we can declare an object like this,
var obj={name:"Irshu",age:22};
console.log(obj);
How do we do the same in c#? the reason i ask because my function need to return a string and a bool together. I dont want to create a class for it, and i dont want to use the dictionary. Are there any alternatives?
public void Message(){
var obj=GetObject(val);
Messagebox.Show(Convert.ToString(obj.ind));
}
public object GetObject(string val){
return new {ind=val,flag=true};
}
This is not valid, is it?
.Net supports ExpandoObject since .NET 4.
http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.110%29.aspx
It lets you declare the object and add properties as your would in javascript.
Traditionally it is for JS interop and I can't recommend it for production work. Tuple<T> is more appropriate as you get strong typing for free. Ultimately you will write less code and see less runtime errors.
What you have in your code is an anonymous type. Anonymous types cannot exist outside the scope in which they are declared. Generally, we use these for transforming LINQ results to temporary objects.
You can't return anonymous types from a method. You can do however something like this:
public void Message(){
var obj = new { ind = "oaiwejf", flag = true };
Messagebox.Show(obj.ind);
}
EDIT
Check this MSDN article
turns out, its posible, one genius on the internet posted this:
public void Message()
{
var obj=GetObject("Irshu");
var y= Cast(obj, new { ind= "", flag= true });
Messagebox.Show(y.ind); //alerts Irshu
}
public object GetObject(string val){
return new {ind=val,flag=true};
}
T Cast<T>(object obj, T type)
{
return (T)obj;
}
I'm having a problem sending a java.util.Date object from my C# client to my java webserver.
When I am calling a WebMethod with a Date WebParam it works. But if I am calling a WebMethod with a custom object that has a Date as WebParam it's always null.
So, this works:
#WebMethod(operationName="thisWorks")
public void thisWorks(#WebParam(name="from")Date from)
{
System.out.println(from); //prints the value of the date
}
This doesn't work:
class MyObj { java.util.Date getMyDate(); }
#WebMethod(operationName="thisDoesntWork")
public void thisDoesntWork(#WebParam(name="myObj")MyObj myObj)
{
System.out.println(myObj.getMyDate()); //prints null
}
Client:
ServiceClient client = new ServiceClient();
client.thisWorks(DateTime.Now);
myObj o = new myObj();
o.myDate = DateTime.Now;
client.thisDoesntWork(o);
The wsdl generates an extra field for the myDate: "bool myDateSpecified". When I set this to true, it works. This is weird, cause when I would have an int field instead of date I also get a specified field for it, but now I dont have to set the specified field for it to work.
This issue seems to be the .Net XML Serializer. I did rewrite my code in Java and it works beautifully.
I can think in a way to workaround the {!variable}Specified = true; sentence:
Move the entire declaration of the object to a separated namespace. So, everytime you update the WSDL your code does not get overwritten.
And do not use the System.Nullable<bool> in the property declaration, use bool or DateTime or Double. Get the point?
I receive the following error when I invoke a custom object
"Object of type 'customObject' cannot be converted to type 'customObject'."
Following is the scenario when I am get this error:
I invoke a method in a dll dynamically.
Load an assembly
CreateInstance....
When calling MethodInfo.Invoke() passing int, string as a parameter for my method works fine => No exceptions are thrown.
But if I try and pass one of my own custom class objects as a parameter, then I get an ArgumentException exception, and it is not either an ArgumentOutOfRangeException or ArgumentNullException.
"Object of type 'customObject' cannot be converted to type 'customObject'."
I am doing this in a web application.
The class file containing the method is in a different project. Also the custom object is a separate class in the same file.
There is no such thing called a static assembly in my code. I am trying to invoke a webmethod dynamically. this webmethod is having the customObject type as an input parameter. So when i invoke the webmethod i am dynamically creating the proxy assembly and all. From the same assembly i am trying to create an instance of the cusotm object assinging the values to its properties and then passing this object as a parameter and invoking the method. everything is dynamic and nothing is created static.. :(
add reference is not used.
Following is a sample code i tried to create it
public static object CallWebService(string webServiceAsmxUrl, string serviceName, string methodName, object[] args)
{
System.Net.WebClient client = new System.Net.WebClient();
//-Connect To the web service
using (System.IO.Stream stream = client.OpenRead(webServiceAsmxUrl + "?wsdl"))
{
//--Now read the WSDL file describing a service.
ServiceDescription description = ServiceDescription.Read(stream);
///// LOAD THE DOM /////////
//--Initialize a service description importer.
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12"; // Use SOAP 1.2.
importer.AddServiceDescription(description, null, null);
//--Generate a proxy client. importer.Style = ServiceDescriptionImportStyle.Client;
//--Generate properties to represent primitive values.
importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
//--Initialize a Code-DOM tree into which we will import the service.
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit1 = new CodeCompileUnit();
unit1.Namespaces.Add(nmspace);
//--Import the service into the Code-DOM tree. This creates proxy code
//--that uses the service.
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1);
if (warning == 0) //--If zero then we are good to go
{
//--Generate the proxy code
CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
//--Compile the assembly proxy with the appropriate references
string[] assemblyReferences = new string[5] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" };
CompilerParameters parms = new CompilerParameters(assemblyReferences);
CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
//-Check For Errors
if (results.Errors.Count > 0)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError oops in results.Errors)
{
sb.AppendLine("========Compiler error============");
sb.AppendLine(oops.ErrorText);
}
throw new System.ApplicationException("Compile Error Occured calling webservice. " + sb.ToString());
}
//--Finally, Invoke the web service method
Type foundType = null;
Type[] types = results.CompiledAssembly.GetTypes();
foreach (Type type in types)
{
if (type.BaseType == typeof(System.Web.Services.Protocols.SoapHttpClientProtocol))
{
Console.WriteLine(type.ToString());
foundType = type;
}
}
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
MethodInfo mi = wsvcClass.GetType().GetMethod(methodName);
return mi.Invoke(wsvcClass, args);
}
else
{
return null;
}
}
}
I can't find anything static in what I do.
Any help is greatly appreciated.
Regards,
Phani Kumar PV
Have you looked at what the proxy class looks like that gets generated? You don't need the proxy to call a web service. Just create a class that inherits from SoapHttpClientProtocol and call Invoke(methodName, params).
You are making this SO much more complicated than you need to. Honestly.
EDIT
If you create a class like this:
public class SoapClient : SoapHttpClientProtocol
{
public SoapClient()
{
}
public object[] Invoke(string method, object[] args)
{
return base.Invoke(method, args);
}
}
and call it like this:
SoapClient soapClient = new SoapClient();
soapClient.Url = webServiceAsmxUrl;
soapClient.Invoke(methodName, args);
I think you will see that it has the exact same results as what you are doing.
Let me try to explain the most probable reason for the problem to come in my approach.
When I invoked a method in the assembly called as "methodname" in the webservice I am trying to pass the parameters required for that as args[] to the function "CallWebService"
This args[] when passed will be successfully working when I try to pass a normal parameters like primitive types including string.
But this is what I did when I tried to pass a custom object as a parameter.
Three things that are done in this.
create an object of that type outside the CallWebService function (using reflection). when I did that way what happens is an instance of the customobject created with a temporary dll name internally.
once I set the set the properties of the object and send it across to the CallWebService function as an object in the args array.
I tired to create an instance of the webservice by creating the dynamic dll.
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
When I finally tried to invoke the method with the instance of the dynamic assembly created
I tried to pass the customobject that is created at step 1,2 via args property.
at the time of invocation the CLR tries to see if the customobject that is passed as input and the method that is being invoked are from the same DLL.
which is obviously not from the way the implementation is done.
So following is the approach that should be used to overcome the problem
I need to create the custom object assembly with the same assembly that I used to the create the webservice instance..
I implemented this approach completely and it worked out fine
MethodInfo m = type.GetMethod(methodName);
ParameterInfo[] pm = m.GetParameters();
object ob;
object[] y = new object[1];
foreach (ParameterInfo paraminfo in pm)
{
ob = this.webServiceAssembly.CreateInstance(paraminfo.ParameterType.Name);
//Some Junk Logic to get the set the values to the properties of the custom Object
foreach (PropertyInfo propera in ob.GetType().GetProperties())
{
if (propera.Name == "AppGroupid")
{
propera.SetValue(ob, "SQL2005Tools", null);
}
if (propera.Name == "Appid")
{
propera.SetValue(ob, "%", null);
}
}
y[0] = ob;
}
this can occur when the version of a dll you have referenced in your reflected code is different from the version of that dll in your compiled code.
This is an old thread, but I just had a similar problem. I looked on here, this one popped up, but I saw no useful solutions.
The OP's error was this: Object of type 'customObject' cannot be converted to type 'customObject'.
My very similar error was this: Object of type 'System.String' cannot be converted to type 'System.Windows.Forms.AccessibleRole'.
Here is how I solved my problem:
I performed a Find and Replace (use CRTL+SHIFT+F to bring the dialog box up) search in the Current Project for the term AccessibleRole.
Within one of the Form's Designer's was a place where I was assigning an AccessibleRole value to a String variable using ToString().
I fixed this, and my problem went away.
I hope this provides help to others.