Protobuf-net deserialize string field to c# guid - c#

In a simplified scenario, assume we have a custom c# type which contains a guid field and the corresponding proto type is,
message HelloReply {
string message = 1;
string guid_id = 2; // this is defined GUID in corresponding c# type
repeated string names = 3;
map<string, double> prices = 4;
}
When I try to deserialize this proto to c# type, I get exception stating 'Invalid wire-type' and a link to explanation which is not helpful to me. Is there a work around for this or am I overlooking something ?

In protobuf-net v3, this changes; there is now a concept called CompatibilityLevel, that works for this scenario; if a Guid member or containing type has CompatibilityLevel of 300 or more, then it is treated as a string. This topic is discussed in depth here: https://github.com/protobuf-net/protobuf-net/blob/main/docs/compatibilitylevel.md

Protobuf-net has opinions on guids. Opinions that were forged back in the depth of time, and that I now regret, but which are hard to revert without breaking people. If I was writing this today with hindsight, yes: it would probably just serialize as a string. But: that isn't what it expects today!
Frankly I'd hack around it with a shadow property. So instead of
[ProtoMember(42)]
public Guid Foo {get;set;}
You could use:
public Guid Foo {get;set;}
[ProtoMember(42)]
private string FooSerialized {
get => Foo.ToString(); // your choice of formatting
set => Foo = Guid.Parse(value);
}

Related

Why does IntelliSense think a value from my dictionary is a dynamic?

Say i have the following method:
private void something()
{
string text = "This is obviously a string";
dynamic pietje = Guid.NewGuid();
var dict = new Dictionary<Guid, string>();
dict.Add(pietje, text);
var someText = dict[pietje];
}
The image below shows IntelliSense still thinks it is a dynamic even though i do not see how this could be anything other than a string (or null)
Am I missing a setting or is there something preventing IntelliSense from knowing someText should be a string? I might be leaning on IntelliSense a bit too much, but it gets quite difficult for some objects to manually type the entire method or property name correctly.
So what is the reason for this? And is there something I can do to resolve this?
obviously I could fix it in many ways:
string someText = dict[pietje];
var someText = dict[(Guid)pietje];
var someText = dict[pietje] as string;
etc.
But that is not the point nor what I want.
This issue crops up in many scenarios. Classic question in SO:
public string Foo(string fooable) { .... }
dynamic fooable = "whatever";
var whyAmIDynamic = Foo(fooable);
Huh? Why is wyAmIDynamic dynamic?!? The compiler should know that wyAmIDynamic is string, shouldn't it?
Yes, but then someone else comes along and writes the following:
public int Foo(int fooable) { .... } //a new overload of Foo
And now, what should Foo(fooable) return? dynamic seems to be the only reasonable option; a method call involving a dynamic argument can't be resolved until runtime.
In your specific case, the compiler has no reason not to believe that someone might come along and implement the following absurd overload to Dictionary<TKey, TValue>:
public int this[string key] { ... }
Does this overload make any sense? No. Is it the compilers business to figure out if it makes sense? No. Is it legal? Yes, therefore the indexer returns a dynamic variable.
Because pietje is dynamic, the result of the execution of var someText = dict[pietje]; is determined on runtime. Until that time, the correctness and outcome of that call is unknown, hence dynamic.
I guess you meant to use var here, since you know the type of pietje on beforehand.
As said in a comment: You go by the simple rule that all dynamic evaluations are done on runtime. You can type in an invalid field name and still let it compile.
You are specifying the Dictionary to be of type Dictionary<Guid, string>. When you try to read, you're using a dynamic as the key for the dictionary, not even knowing if this is would be a valid statement.
There's a relative easy way of fixing this issue:
Address the key in the dictionary with the same type as the declaration
string text = "This is obviously a string";
dynamic pietje = Guid.NewGuid();
var dict = new Dictionary<Guid, string>();
dict.Add(pietje, text);
var someText = dict[(Guid)pietje];
//Of course you could go about casting this in numerous ways

Can I access fields of an Object in C#

I am using Newtonsoft.Json.JsonConvert.DeserializeObject(string str) to translate a string into a .Net Object. The real type of this string can be multiple, and there's no other information to indicate the type of this string. But I can confirm that the string message is a derived class object of a common class like Message, and fields in Message can however tell the real type, Message has a field like int type. The string is MessageA or MessageB or whatever all with a different type.
If I translate it into an Object, I can saw in the visual studio debugger that this Object have exactly the fields described in the Json string. But I cant access these fields. And cast this Object into a Message will failed with a bad cast.
What I am doing now is first translate the string into a Message and to see the type, then I translate again. It's not preferred. So can I just translate it into something that I can read all of the data? Another option is Dictionary, but I have some numerical fields. Any other suggestions?
JsonConvert.DeserializeObject(string str), when used on a JSON object, returns a JObject. You can use this directly (e.g. use DeserializeObject<JObject> to explicitly type it), or as a dynamic, to access its properties, e.g.
var data = #"{""type"": 1, ""otherProperty"": ""Hello!""}";
dynamic obj = JsonConvert.DeserializeObject(data);
if (obj.type == 1)
{
Console.WriteLine(obj.otherProperty); // prints Hello!
}
Also, you mention numeric fields as being a problem with working with a dictionary, but if you make it a Dictionary<string, dynamic> you might find it easier to work with:
var data = #"{""type"": 2, ""otherProperty"": 5}";
var dict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(data);
if (dict["type"] == 2)
{
int i = (int)dict["otherProperty"]; // type is long, so cast is required if you want an int
Console.WriteLine(i); // prints 5
}

Incorrect serialization in C# using JSON.NET

I am attempting to take an object structured like so
public class Item
{
public Guid SourceTypeID {get;set;}
public Guid BrokerID {get;set;}
public double Latitude {get;set;}
public double Longitude {get;set;}
public DateTime TimeStamp {get;set;}
public object Payload {get;set;}
}
and serialize it with JSON.NET using a call like:
Item expected = new Item()
{
SourceTypeID = Guid.NewGuid(),
BrokerID = Guid.NewGuid(),
Latitude = 33.657145,
Longitude = -117.766684,
TimeStamp = DateTime.Now,
Payload = new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
};
string jsonString = JsonConvert.SerializeObject(expected);
The payload member of the Item object will potentially hold any one primitive of a list of C# primitives (plus a few others like Guid), or an array of those types (as in the example, a byte array), or a "flat" object composed of any of those previously listed "primitives" (dynamically created).
When I perform the SerializeObject() call, the string that is produced contains:
{"Payload":"AAECAwQFBgcICQ==","SourceTypeID":"d8220a4b-75b1-4b7a-8112-b7bdae956a45",
"BrokerID":"951663c4-924e-4c86-a57a-7ed737501dbd",
"TimeStamp":"\/Date(1328202421559-0600)\/",
"Latitude":33.657145,"Longitude":-117.766684}
However when I make the deserializing call the item that is produced is partially incorrect
Item actual = JsonConvert.DeserializeObject<Item>(jsonString);
actual.SourceTypeID : {00000000-0000-0000-0000-000000000000}
actual.BrokerID : {951663c4-924e-4c86-a57a-7ed737501dbd}
actual.Latitude : 33.657145;
actual.Longitude : -117.766684;
actual.TimeStamp : {2/2/2012 11:07:01 AM}
actual.Payload : null
The SourceTypeID member (Guid), and the Payload member (object, holding a byte[]), are both incorrect. The serialized string seems to hold the right identity for the guid, but not for the byte array.
I see that an alternate signatureof SerializeObject is
SerializeObject(object value, params JsonConverter[] converters);
Is this the correct way to inform the de/serialization engine about types that it apparently handles wrong? Since I am working with a limited set of "primitives" at the core of my object, if I could create a set of converters for each of those types, would that solve my problem?
I want to avoid reinventing the wheel if possible, as it seems that this would be a problem that has already been handled gracefully, and I'd like to leverage something like that if available.
Payload will be deserialized as a string unless you place a [JsonProperty] attribute with TypeNameHandling enabled on it, otherwise the deserializer won't know what to deserialize it as.
I haven't been able to duplicate the problem you got with some properties being null using the latest source code at http://json.codeplex.com/SourceControl/list/changesets
This appears to be an actual bug in JSON.NET.
It's not idea, but a workaround might be to have a two-stage serialization. The first, the object that actually gets serialized to/from JSON, would consist only of fields/properties with string type. The second object (the one your server-side code would use) would be an object that is converted to strong types manually.
Not ideal, but possible. Additonally, you could consider using the DataContractJsonSerializer, which does a pretty good job of handling byte arrays and guids IME. Dates are still pretty sucky, but that's mostly the fault of Javascript.

C#: run-time datatype conversion

This is my first time using StackOverflow myself. I have found many answers to many of my question here before, and so I thought I would try asking something myself.
I'm working on a small project and I am a bit stuck right now. I know ways to solve my problem - just not the way I want it to be solved.
The project includes an NBT parser which I have decided to write myself since it will be used for a more or less custom variation of NBT files, though the core principle is the same: a stream of binary data with predefined "keywords" for specific kinds of tags. I have decided to try and make one class only for all the different types of tags since the structure of the tags are very similar - they all contain a type and a payload. And this is where I am stuck. I want the payload to have a specific type that, when an explicit conversion is done implicitly, throws an error.
The best I could come up with is to make the payload of type Object or dynamic but that will allow all conversions done implicitly:
Int64 L = 90000;
Int16 S = 90;
dynamic Payload; // Whatever is assigned to this next will be accepted
Payload = L; // This fine
Payload = S; // Still fine, a short can be implicitly converted to a long
Payload = "test"; // I want it to throw an exception here because the value assigned to Payload cannot be implicitly cast to Int64 (explicit casting is ok)
Is there any way of doing this? I would like to solve it by somehow telling C# that from now on, even though Payload is dynamic, it will throw an exception if the assigned value cannot be implicitly converted to the type of the current value - unless, of course, it is done explicitly.
I am open to other ways of accomplishing this, but I would like to avoid something like this:
public dynamic Payload
{
set
{
if(value is ... && Payload is ...) { // Using value.GetType() and Payload.GetType() doesn't make any difference for me, it's still ugly
... // this is ok
} else if(...) {
... // this is not ok, throw an exception
}
... ... ...
}
}
Have you considered using generics?
This would automatically give you compile-time checking on which conversions are allowed.
class GenericTag<T>
{
public GenericTag(T payload)
{
this.Payload = payload;
}
public T Payload { set; get; }
}
// OK: no conversion required.
var tag2 = new GenericTag<Int64>(Int64.MaxValue);
// OK: implicit conversion takes place.
var tag1 = new GenericTag<Int64>(Int32.MaxValue);
// Compile error: cannot convert from long to int.
var tag4 = new GenericTag<Int32>(Int64.MaxValue);
// Compile error: cannot convert from string to long.
var tag3 = new GenericTag<Int64>("foo");
If you know you will need Int64, why not to use Convert.ToInt64?

How do I access the enumerated item with an indexer and assign array string to it for display?

EDITED: Updated 3/23/09. See rest of post at bottom. I'm still having trouble with the indexer. Anymore help or examples would really help me out.
Write a class, MyCourses, that contains an enumeration of all the
courses that you are currently taking.
This enum should be nested inside of
your class MyCourses. Your class
should also have an array field that
provides a short description (as a
String) of each of your courses. Write
an indexer that takes one of your
enumerated courses as an index and
returns the String description of the
course.
Write a class MyFriends that contains an indexer that provides
access to the names of your friends.
namespace IT274_Unit4Project
{
public class MyCourses
{
// enumeration that contains an enumeration of all the courses that
// student is currently enrolled in
public enum CourseName {IT274= 0,CS210 = 1}
// array field that provides short description for each of classes,
// returns string description of the course
private String[] courseDescription =
{"Intermediate C#: Teaches intermediate elements of C# programming and software design",
"Career Development Strategies: Teaches principles for career progression, resume preparation, and overall self anaylsis"};
// indexer that takes one of the enumerated courses as an index
// and returns the String description of the course
public String this[CourseName index]
{
get
{
if (index == 1)
return courseDescription[0];
else
return courseDescription[1];
}
set
{
if (index == 1)
courseDescription[0] = value;
else
courseDescription[1] = value;
}
}
}
}//end public class MyCourses
I'm working on this homework project and having trouble understanding the text explaining how to correctly take the accessed value of the enumeration and then apply the string array value to it. Can you please help me understand this? The text we are using is very difficult and poorly written for a beginner to understand, so I'm kind of on my own here. I've got the first parts written, but need some help on the accessing of the enumeration value and assigning, i think i'm close, but don't understand how to properly get and set the values on this.
Please do not provide me with direct code answers, unless a MSDN style explanation that is generalized and not specific to my project. ie:
public class MyClass
{ string field1;
string field2;
//properties
public string Value1
get etc...
Thanks!
First of all, the base type of an enumeration has to be a numeric value type, so you can't have an enumeration with base type string. The following isn't going to compile:
public enum CourseName
{
Class1 = "IT274-01AU: Intermediate C#",
Class2 = "CS210-06AU: Career Development Strategies"
}
So change it to use the default base type of int. Something like the following will do, but change the names as you see fit (you might want to use the course name instead of the code, for example). Remember also that you should use meaningful names whenever possible in an enumeration.
public enum Courses
{
IT274_01AU,
CS210_06AU
}
(I know you said you didn't want specific code examples, but I think this one illustrates my point much more clearly than any explanation.)
Second, you're on the right track with the indexer, but you have to think of how to relate the enumeration to the array of string descriptions. Remember, an enumeration is nothing more than a finite set of glorified (named) numbers. With the above Courses enumeration, you have two values named IT274_01AU and CS210_06AU. So in the indexer, you have to map each of these values to the string description. There are multiple ways to do it, but the simplest one would be a switch statement, for example:
switch (myEnum)
{
case value1:
return string1;
case value2:
return string2;
}
Another option, however is to explicitly map the enum values to its base type, and use the base type to index into your array. For example, if you have the enum
public enum Enumerations
{
value1 = 0,
value2 = 1
}
then you can index directly into an array using myArray[(int)myEnum]. This may be of use and is the slightly-more-advanced-but-less-lines-of-code-and-arguably-easier-to-understand method.
(resisting the urge to write code)
First off, an enumeration is a named list of integers and (per MSDN) the approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.
Also, remember that courseDescription is an array of strings and the purpose of the indexer is to give you an index into that array (ie. [0] returns the first string, [1] returns the second, etc.).

Categories

Resources