XmlReader.ReadContentAsObject always returns string type - windows-phone-7

According to the MSDN documentation, XMLWriter.WriteValue writes xsd type information to the xml for simple CLR types. Then XMLReader.ReadContentAsObject supposedly reads out the appropriately-typed object when the XML is parsed. However, this always seems to return a string object for me and the ValueType property of the XMLReader is string. I've tried inserting longs and DateTimes, but they always end up as strings. Any ideas what I'm doing wrong or is this a Windows Phone bug?
XML Writing Code
public void WriteXml(XmlWriter writer) {
// KeyValuePair<string, object> pair initialized previously
writer.WriteStartElement(pair.Key);
writer.WriteValue(pair.Value)
writer.WriteEndElement();
}
XML Parsing Code
public void ReadXml(XMLReader reader) {
while (reader.Read()) {
if (reader.NodeType == XmlNodeType.Element) {
Type T = reader.ValueType; // T is string
reader.ReadStartElement();
object o = reader.ReadContentAsObject(); // o is string
o = reader.ReadContentAs(T, null); // o is string
}
}
}

You need to use a schema file (XSD) so that the framework can infer the type of a node. Otherwise ValueType will always return System.String.

MSDN says:
If a validation error occurs while parsing the content and the reader is an XmlReader object created by the Create method, the reader returns the content as a string. In other words when a validation error or warning occurs, the content is considered to be untyped.

I was making this too difficult. My goal was to serialize a Dictionary with generic type (string, object) by traversing its KeyValuePairs , but that class doesn't seem to be serializeable using XmlSerializer. I just created another class with two public properties, Key and Value, so I could use XmlSerializer. When deserializing with XmlSerializer, the type of Value is restored as long as it is a supported CLR type.
public void WriteXml(XmlWriter writer) {
// KeyValuePair<string, object> pair initialized previously
writer.WriteStartElement("entry");
MyClass toSerialize = new MyClass(pair.Key, pair.Value);
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
serializer.Serialize(writer, toSerialize);
writer.WriteEndElement();
}

Related

.net 5.0 signature change of System.Text.Json.JsonSerializer.Deserialize()

I am attempting the step from .NET Core 3.1 to .NET 5.0 and get a bunch of nullability warnings at the uses of Deserialize<TValue>(String, JsonSerializerOptions). A quick investigation shows that the signature has changed from
public static TValue Deserialize<TValue> (string json, System.Text.Json.JsonSerializerOptions options = default); (doc) in .NET Core 3.1 to
public static TValue? Deserialize<TValue> (string json, System.Text.Json.JsonSerializerOptions? options = default); (doc) in .NET 5.0.
It looks as a reasonable change, but I haven't been able to provoke a null to actually be returned, since all bad input/bad use will throw an exception in my experiments, and the documentation does not describe why the call would return a null as far as I can tell.
It seems a bit unnecessary to add null return checks to all our uses, if failed deserialization will throw rather than returning null.
What am I missing?
As shown in the original JSON proposal, the text null is perfectly well-formed JSON:
A value can be a string in double quotes, or a number, or true or false or null, or an object or an array. These structures can be nested.
This is further clarified in RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format which states that a well-formed JSON text need be nothing more than a single primitive value including null:
A JSON text is a sequence of tokens. The set of tokens includes six structural characters, strings, numbers, and three literal names [false, true and null].
A JSON text is a serialized value. Note that certain previous specifications of JSON constrained a JSON text to be an object or an array. Implementations that generate only objects or arrays where a JSON text is called for will be interoperable in the sense that all implementations will accept these as conforming JSON texts.
Since null is a well-formed JSON text according to this most recent JSON RFC, JsonSerializer will not throw when deserializing it to a reference type or nullable value type, and will instead just return a null value:
object? obj1 = JsonSerializer.Deserialize<object>("null"); // Does not throw; explicitly typed for clarity.
Assert.IsNull(obj1); // Passes
var array = JsonSerializer.Deserialize<int []>("null"); // Does not throw;
Assert.IsNull(array); // Passes
var nullable = JsonSerializer.Deserialize<int?>("null"); // Does not throw;
Assert.IsNull(nullable); // Passes
Conversely the following generates a compiler warning:
#nullable enable
object obj2 = JsonSerializer.Deserialize<object>("null"); // Compiler warning: Converting null literal or possible value to non-nullable type;
And the following throws, since an int is a non-nullable value type to which null cannot be assigned:
var i = JsonSerializer.Deserialize<int>("null"); // Throws, since int is a non-nullable value type.
If you want to an exception to be thrown when deserializing the JSON text null, you could add the following extension method:
public static class ObjectExtensions
{
public static T ThrowOnNull<T>(this T? value) where T : class => value ?? throw new ArgumentNullException();
}
And do:
var value = JsonSerializer.Deserialize<TValue>(json).ThrowOnNull();
Demo fiddle here.

Unable to cast object of type ‘System.String’ to type ‘AuthBotES.ReturnIntents’

Currently I had integrate LUIS with Bot Framework v4.
When I search for result match with Intent,
the Bot return me with this Error:
Error : Unable to cast object of type ‘System.String’ to type ‘AuthBotES.ReturnIntents’.
My Source code as below:
if (stepContext.Result != null)
{
var result = (ReturnIntents)stepContext.Result;
var msg = $"{result}";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
}
and my ReturnIntents classes.
public class ReturnIntents
{
public string Intent { get; set; }
public double Score { get; set; }
public string Entities { get; set; }
}
A few issues here:
The first code block you posted looks to be for handling the result of a dialog, not for processing a LUIS result.
The cast from string to ReturnIntents will always fail.
Even if your cast of stepContext.Result to ReturnIntents did work, your msg variable would only contain namespace.to.class.ReturnIntents (the string representation of the object type, not the string representation of the objects properties.
Your msg variable is redundant.
I will address these in the order that they occur.
1 - Incorrect code block
This block of code looks suspiciously like code used to process a dialog and here e.g.:
var result = (bool)stepContext.Result;
Rather than the code for handling a LUIS result e.g.:
var dispatchResult = await cognitiveModels.DispatchService.RecognizeAsync(dc.Context, CancellationToken.None);
2 - Casting error
The error is telling you that it doesn't know how to convert a string object to a ReturnIntents object. To convert the string to your object you could use a couple of methods:
Use the NewtonSoft.Json NuGet package to allow you to turn the JSON string into your object as explained here.
2) A user defined type conversion as detailed in the official docs here and explained in this answer.
This error is a red herring in terms of your solution because I believe you're accidentally copied in the wrong block of code.
3 - Incorrect ToString behaviour
To get the string value of a ReturnIntents you will need to override the ToString method for the class and write your own custom implementation.
4 - Redundant cast
This:
// We know that this cast fails, and that stepContext.Result is a string
var result = (ReturnIntents)stepContext.Result;
// This will only return <namespace.path>.ReturnIntents (if the cast above works)
var msg = $"{result}";
// Passing in message msg isn't required, we can just pass in stepContext.Result
await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
Becomes:
var result = stepContext.Result;
await stepContext.Context.SendActivityAsync(MessageFactory.Text(result), cancellationToken);
So what I actually think you actually want is the following:
var dispatchResult = await cognitiveModels.DispatchService.RecognizeAsync<ReturnIntents>(dc.Context, CancellationToken.None);
Which will send the user's input off to LUIS, and deserialize the response into a ReturnIntents object.
Edit to provide solution to the OP
The ExecuteLuisQuery method called here, and defined here returns a ReturnIntents object.
This object is passed as an option to the ReturnIntentDialog here. Because this comes through as an instance of the object type you have a few options inside your FinalStepAsync method here to turn your options object into a ReturnIntents object.:
Casting
ReturnIntents returnIntents = null;
if (stepContext.Options is ReturnIntents)
{
returnIntents = (ReturnIntents)stepContext.Options;
}
Deserializing
using Newtonsoft.Json;
ReturnIntents returnIntents = null;
if (stepContext.Options is ReturnIntents)
{
returnIntents = JsonConvert.DeserializeObject<ReturnIntents>(JsonConvert.SerializeObject(stepContext.Options));
}

String cannot be cast to an Iterable error?

So I'm attempting to go through a groovyObject's fields and obtain the property of that field. So this is what I got(sorry its a little rough so cleaning would be appreciated but not necessary, I'm also doing a little debugging and other stuff with the Log and what not.):
public void traverse(final GroovyObject groovy) throws RepositoryException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
{
Field[] theFields = groovy.getClass().getDeclaredFields();
final ArrayList<Field> fields = new ArrayList<Field>();
int count =0;
for(Field field : theFields)
{
fields.add(field);
LOG.error("{} = {}",field.getName(), groovy.getProperty(field.getName()));
}
//this is the guava tree traverser
TreeTraverser<GroovyObject> traverser = new TreeTraverser<GroovyObject>()
{
#Override
public Iterable<GroovyObject> children(GroovyObject root)
{
return (Iterable<GroovyObject>)root.getProperty(fields.get(0).getName());
//|-->Here I get the String cannot be cast to Iterable. Which I find odd since it is still an object just getProperty takes a string. right?
}
};
Thoughts on this? Thanks for the help!
GroovyObject.getProperty(String) retrieves the value of the given property. And if that value happens to be a String you cannot cast it to Iterable.
If you adjust your log statement, you can inspect the types of the fields:
LOG.error("{} of type {} = {}", field.getName(), field.getType(), groovy.getProperty(field.getName()));
So I figured it outl. Essentially what needs to happen is I need to make two iterators: one for the groovy objects and one for the property strings so the end goal looks like
groovyObject.iterate().next().getProperty(string.iterate().next());
Or something like that, I will update this when I figure it out.!
Once I make that I can go back in and think about making it more efficient

How to convert ODataEnumValue into its CLR enum type?

While traversing the expression tree from a FilterQueryOption, I have an instance of a ODataEnumValue. I'm wondering how to convert that into the corresponding CLR enum type value in generic way (i.e. without having to look-up the actual CLR type myself).
Maybe you can refer to ODataEnumDeserializer's ReadInline method, which convert ODataEnumvalue to CLR enum type if it is in EdmModel.
The enum type can be retrieved by getting the ClrTypeAnnotation of the TypeReference as follows. First get the model from the FilterQueryOption instance:
IEdmModel _model = filterQueryOption.Context.Model;
Then, later when parsing its FilterClause, for example for a ConstantNode:
private object GetClrValue(ConstantNode constantNode)
{
ODataEnumValue enumValue;
...
else if ((enumValue = constantNode.Value as ODataEnumValue) != null)
{
var annotation = _model.GetAnnotationValue<ClrTypeAnnotation>(constantNode.TypeReference.Definition);
Type enumType = annotation.ClrType;
parameterValue = Enum.Parse(enumType, enumValue.Value);
}
...
}

Unable to deserialize ArrayOfString back to List<string> in C#

I have seen this question asked, but have not found answers that work so I am asking it again.
I have a restful web service with a POST method that returns a serialized List.
<ArrayOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<string>QoL5vA8OKgydWIn%2fdWiu70nobBrJo4N9iXeHmtM7Aj4%3d</string>
<string>vxHyJiSSSvDSZWXOdcfl5FMQC97xxGEWDO8Zy8Qp3X5CwADUbEE8ifACQHR1n7uamEaIUbf85ZuBDB8FFCNY2tJAMJ2jNw09SqGVTpMU5uI06DLtuYnJqsPIF735NOhlvRhBLPnpp62DFMCVsDNHy55UBF3Ggi06ZWTiJ8LTYIf3FYiFLPpXLZ1wWeE5chAWQGfz7zDYJa1OgSZ</string>
<string>OqGAT7Yqe6DfyVD29BeIXFyGtabVCloaC9FA1Fp20JkJ9T17ZzyqhnGxwWda7FqqyJUM8YK9OdcOCgTYrl4JxalECdtJm75TdSG8IAPQlFHp6Gidg4EwZaO9FKahlYMm5JrFpxTmLrdLgMAkYEV7gIR6zIyIByAGwYYDDwH3QCHrym3CuueRnFWAHCJu1LJbx0zRtt7g3yEaTiJ</string>
</ArrayOfstring>
The code performing the call is as follows below.
XDocument xDocResponse = RestPOSTToXDocument(szBaseUri, szInput);
string szNamespace = xDocResponse.Root.Name.Namespace.ToString();
IXmlUtils utility = new XmlUtils();
List<string> lst = utility.DeserializeList<string>(xDocResponse, szNamespace);
I have also attempted a different version of the call as shown below.
List<List<string>> lst = utility.DeserializeList<List<string>>(xDocResponse, szNamespace);
Both versions of the calls generate the error provided below.
An exception of type 'System.InvalidOperationException' occurred in System.Xml.dll but was not handled in user code
Additional information: There is an error in XML document (0, 0).
My Deserialize method is provided below.
public List<T> DeserializeList<T>(XDocument doc, string szNamespace)
{
List<T> result = null;
XmlSerializer serializer = new XmlSerializer(typeof(List<T>), szNamespace);
using (System.Xml.XmlReader reader = doc.CreateReader())
{
result = (List<T>)serializer.Deserialize(reader);
}
return result;
}
The XML within the XDocument is valid, so I do not understand why this error is being generated. Also, even if I attempt to get the elements via navigation in the XDocument, it does not work. If I look at the doc.Root.value, it appears that all of the strings are concatenated together into a single string.
Does anyone have any information on how I may deserialize this XDocument into a List?
Thanks to the assistance of mellamokb, I found the problem. When my List is serialized, it is serialized into an ArrayOfstring. If I take the XML and replace ArrayOfstring with ArrayOfString, the deserialization works.
This does not make much sense. I would welcome an explanation of why this is necessary though it may have something to do with String vs string.
Essentially, I had to do the following:
string szXml = xDocResponse.ToString();
int nEndRoot = szXml.IndexOf(">");
szXml = szXml.Replace("ArrayOfstring", "ArrayOfString");
xDocResponse = utility.LoadXDocument(szXml);
string szNamespace = xDocResponse.Root.Name.Namespace.ToString();
List<string> lst = utility.DeserializeList<string>(xDocResponse, szNamespace);
Simply get the response stream and deserealize into string array and store it to List
Code Snippet:
List<string> listNew=new List<string>();
using (Stream answer = webResponse.GetResponseStream())
{
DataContractSerializer xmlSer = new DataContractSerializer(typeof(string[]));
var stringArr= (string[])xmlSer.ReadObject(answer);
foreach (string item in GenreList)
{
listNew.Add(item);
}
}

Resources