I have the following code:
//Models
public class Home{
public Guid HomeId { get; set; }
public Guid UserId { get; set; }
public string Name { get; set; }
public byte[] Description1 { get; set; }
public virtual User User { get; set; }
}
public class User
{
public Guid UserId { get; set; }
public string Email { get; set; }
public virtual Perfil Perfil { get; set; }
}
public class Perfil
{
public Guid UserId { get; private set; }
public string Name { get; private set; }
public byte[] Description2 { get; set; }
}
// JsonConverter
public class ByteArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(byte[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var m = Convert.FromBase64String((string)reader.Value);
return m;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
byte[] bytes = (byte[])value;
writer.WriteValue(Convert.ToBase64String(bytes));
}
}
// Serialize (Calls the WriteJson function of the ByteArrayConverter, OK)
var serialized = JsonConvert.SerializeObject(obj, settings);
{
"HomeId": "2925362b",
"UserId": "9ea43c30",
"Name": "Name 1",
"Description1": "VABlAHMAdABlAA==", //===> Converts to Base64 (OK)
"User": {
"UserId": "9ea43c30",
"Email": "aaaa#aaaa.com",
"Perfil": {
"UserId": "9ea43c30",
"Name": "Name 2",
"Description2": "dABlAHMAdABlAA==", //===> Converts to Base64 (OK)
}
}
}
// Deserialize
var deserialized = JsonConvert.DeserializeObject<T>(serialized, settings); // T is class Home
{
"HomeId": "2925362b",
"UserId": "9ea43c30",
"Name": "Name 1",
"Description1": "VABlAHMAdABlAA==", //===> Don't Convert from Base64, (**Does not call the ReadJson function of ByteArrayConverter**)
"User": {
"UserId": "9ea43c30",
"Email": "aaaa#aaaa.com",
"Perfil": {
"UserId": "9ea43c30",
"Name": "Name 2",
"Description2": "dABlAHMAdABlAA==", //===> Convert from Base64 (OK)
}
}
}
Settings
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
settings.Converters.Add(new ByteArrayConverter());
The Description1 property does not get into the ReadJson function of the ByteArrayConverter class, so it does not convert back the byte [], generating another invalid [] byte, ....
Any idea of this problem?
Old question, but need clarification
when I do not use as above, it serializes a byte[] type with the information: $ type: "System.Byte [], mscorlib" $ Value: "VABlAHMAdABlAA ==", but when I go deserialize it shows an error: Unexpected character encountered while parsing value
To avoid the above error in deserialization, use the following json setting in deserialization.
TypeNameHandling = TypeNameHandling.None
TypeNameHandling setting include type information when serializing JSON and read type information so that the create types are created when deserializing JSON
For more details read: TypeNameHandling Enumeration
The serialization of byte[] is Base-64 string, and when deserialized it generate byte[] again.
You need not ByteArrayConverter as given in the comments of #Brian Rogers and #dbc
You can find a complete working example using your code with modification online
Related
When using the request/response pattern, during the deserialization of the response, an incompatibility error occurs. Apparently, the publisher serializer configuration is causing this issue, although the message is as expected.
Using the JsonProperty feature could isolate the problem, however, it does not reflect what was expected.
Versions
.NET: 6.0;
MassTRansit: 7.3.0;
Newtonsoft: 13.0.1
Type specified in JSON 'Messages.Models+CreditCard, Messages'
is not compatible with 'GreenPipes.DynamicInternal.Models\+Messages.Models\+IPaymentMethod'
Resource:
_bus.CreateRequestClient<TMessage>().GetResponse<TResponse>(message, cancellationToken);
Publisher serializer configuration:
bus.ConfigureJsonSerializer(settings =>
{
settings.TypeNameHandling = TypeNameHandling.Objects;
return settings;
});
Message response serialization:
{
"message": {
"$type": "Messages.Services.ShoppingCarts.Responses+CartDetails, Messages",
"cartItems": [
{
"$type": "Messages.Models+Item, Messages",
"productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"productName": "string",
"unitPrice": "10.0",
"quantity": 30,
"pictureUrl": "string"
}
],
"paymentMethods": [
{
"$type": "Messages.Models+CreditCard, Messages",
"id": "be7c40ac-1cd1-4e35-bccf-a1a2f4efecfd",
"expiration": "01/22",
"holderName": "string",
"number": "374245455400126",
"securityNumber": "string"
},
{
"$type": "Messages.Models+PayPal, Messages",
"id": "9465cf12-a322-477d-94c8-116d03a8399e",
"Password": "123",
"UserName": "string"
}
],
...
}
}
Consumer deserializer configuration:
bus.ConfigureJsonDeserializer(settings =>
{
settings.TypeNameHandling = TypeNameHandling.Objects; // or Auto, All, etc...
return settings;
});
Error:
System.Runtime.Serialization.SerializationException: A JSON serialization exception occurred while deserializing the message envelope
---> Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'Messages.Models+CreditCard, Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
is not compatible with 'GreenPipes.DynamicInternal.Models\+Messages.Models\+IPaymentMethod, MessagesGreenPipes.DynamicInternale7ccc67139ad479db488c4fa6310335a, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Path 'message.paymentMethods[0].$type', line 30, position 55.
JsonProperty does not work:
public record CartDetails : Response
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public IEnumerable<Models.IPaymentMethod> PaymentMethods { get; init; }
}
{
"message": {
"cartItems": [
{
"productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"productName": "string",
"unitPrice": "10.0",
"quantity": 30,
"pictureUrl": "string"
}
],
"paymentMethods": [
{
"id": "be7c40ac-1cd1-4e35-bccf-a1a2f4efecfd",
"expiration": "01/22",
"holderName": "string",
"number": "374245455400126",
"securityNumber": "string"
},
{
"id": "9465cf12-a322-477d-94c8-116d03a8399e",
"Password": "123",
"UserName": "string"
}
],
...
}
}
Response
public record CartDetails : Response
{
// [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public IEnumerable<Models.IPaymentMethod> PaymentMethods { get; init; }
public IEnumerable<Models.Item> CartItems { get; init; }
public Guid UserId { get; init; }
public decimal Total { get; init; }
public Guid Id { get; init; }
public bool IsDeleted { get; init; }
}
Types:
public interface IPaymentMethod
{
Guid Id { get; }
decimal Amount { get; }
}
public record CreditCard : IPaymentMethod
{
public Guid Id { get; init; }
public decimal Amount { get; init; }
[property: JsonConverter(typeof(ExpirationDateOnlyJsonConverter))]
public DateOnly Expiration { get; init; }
public string HolderName { get; init; }
public string Number { get; init; }
public string SecurityNumber { get; init; }
}
public record PayPal : IPaymentMethod
{
public Guid Id { get; init; }
public decimal Amount { get; init; }
public string UserName { get; init; }
public string Password { get; init; }
}
Thanks Wiktor Zychla, the issue led me to Nkot's workaround, which served me very well:
TypeNameHandlingConverter:
internal class TypeNameHandlingConverter : JsonConverter
{
private readonly TypeNameHandling _typeNameHandling;
public TypeNameHandlingConverter(TypeNameHandling typeNameHandling)
{
_typeNameHandling = typeNameHandling;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
=> new JsonSerializer { TypeNameHandling = _typeNameHandling }.Serialize(writer, value);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
=> new JsonSerializer { TypeNameHandling = _typeNameHandling }.Deserialize(reader, objectType);
public override bool CanConvert(Type objectType)
=> IsMassTransitOrSystemType(objectType) is false;
private static bool IsMassTransitOrSystemType(Type objectType)
=> objectType.Assembly == typeof(IConsumer).Assembly ||
objectType.Assembly.IsDynamic ||
objectType.Assembly == typeof(object).Assembly;
}
Consumer deserializer configuration:
bus.ConfigureJsonDeserializer(settings =>
{
settings.Converters.Add(new TypeNameHandlingConverter(TypeNameHandling.Objects));
return settings;
});
I have a class that has a list of objects as seen in the first class below.
public class AddComponentToChangeGroupParams
{
[JsonProperty]
static string Id = "1";
public List<Component> Component{ get; set; }
}
public class Component
{
public string Name { get; set; }
public List<ControlName> Controls { get; set; }
}
public class ControlName
{
public string Name { get; set; }
}
I need this to output:
{
"Id": 1,
"Component": {
"Name": "nameOfComponent",
"Controls": [
{"Name": "name of control"},
{"Name": "name of control 2"}
]
},
"Component": {
"Name": "nameOfComponent2",
"Controls": [
{"Name": "name of control"},
{"Name": "name of control 2"}
]
}
}
Any help with this would be greatly appreciated.
Turns out it can be done using JsonConverter:
public class AddComponentToChangeGroupParamsConverter : JsonConverter
{
public override bool CanWrite => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var addComponentToChangeGroupParams = (AddComponentToChangeGroupParams)value;
writer.WriteStartObject();
writer.WritePropertyName("Id");
// I made Id public for simplicity you can use reflection to get value if you want
writer.WriteValue(AddComponentToChangeGroupParams.Id);
foreach (var component in addComponentToChangeGroupParams.Component)
{
writer.WritePropertyName("Component");
serializer.Serialize(writer, component);
}
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
As you can see you take full responsibility for serializing AddComponentToChangeGroupParams class so you need to write all properties in this converter.
To enable this add [JsonConverter(typeof(AddComponentToChangeGroupParamsConverter))] on AddComponentToChangeGroupParams.
I want to deserialize (and afterwards serialize again) the following Json
{
"id": "12345",
"custom_fields": [
{
"definition": "field1",
"value": "stringvalue"
},
{
"definition": "field2",
"value": [ "arrayvalue1", "arrayvalue2" ]
},
{
"definition": "field3",
"value": {
"type": "user",
"id": "1245"
}
}
]
}
The type of the value if different (field1: string, field2: array, field3: an extra nested structure).
This what I have:
public class CustomField
{
public string Definition { get; set; }
public object Value { get; set; }
}
public class RootObject
{
public string Id { get; set; }
public List<CustomField> Custom_fields { get; set; }
}
The Value-field is defined as object, but this is a problem when serializing again, especially the nested structure for field3.
How can I solve this?
You could implement a custom converter for your Value property:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Test
{
public class Program
{
static void Main(string[] args)
{
var json =
#"{
""id"": ""12345"",
""custom_fields"": [
{
""definition"": ""field1"",
""value"": ""stringvalue""
}, {
""definition"": ""field2"",
""value"": [ ""arrayvalue1"", ""arrayvalue2"" ]
}, {
""definition"": ""field3"",
""value"": {
""type"": ""user"",
""id"": ""1245""
}
}
]
}";
var rootObject = JsonConvert.DeserializeObject<RootObject>(json);
Console.WriteLine(JsonConvert.SerializeObject(rootObject));
}
}
public class RootObject
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "custom_fields")]
public List<CustomField> CustomFields { get; set; }
}
public class CustomField
{
[JsonProperty(PropertyName = "definition")]
public string Definition { get; set; }
[JsonProperty(PropertyName = "value")]
[JsonConverter(typeof(CustomValueConverter))]
public dynamic Value { get; set; }
}
public class CustomValueConverter : JsonConverter
{
// By returning false, we let the default `WriteJson` kick in
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return string.Empty;
}
if (reader.TokenType == JsonToken.String)
{
return serializer.Deserialize<string>(reader);
}
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<string[]>(reader);
}
return serializer.Deserialize<Dictionary<string, string>>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
I am trying to deserialize JSON data in C# using Newtonsoft namespace.
Here are my classes:
class lastResponse
{
public string type { get; set; }
public Metadata metadata { get; set; }
// public string[] course { get; set; }
public List<object> course { get; set; }
public string publisher { get; set; }
}
public class Metadata
{
public string bookID { get; set; }
public string title { get; set; }
public string filename { get; set; }
}
This code:
var errorMsg = JsonConvert.DeserializeObject<lastResponse>(downloader.LastResponse);
Gives me this error:
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll Additional information: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'BookManager.lastResponse' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
Please help me figure out what I'm missing here.
The most possible reason for this could be the json you're getting from downloader.LastResponse is not an array.
Your Json could be like this.
{
"type": "First",
"metadata": {
"bookID": "book123",
"title": "bookTitle Here",
"filename": "book file name"
},
"course": [
{
"1": "01",
"2": "02"
},
{
"001": "001",
"002": "002"
}
],
"publisher": "you"
}
What you need to change is either pass JSON array in downloader.LastResponse or you can change your deserialize object statement as follows.
JsonConvert.DeserializeObject<lastResponse>(downloader.LastResponse);
According to what you're doing the correct JSON will be like this.
**[**
{
"type": "First",
"metadata": {
"bookID": "book123",
"title": "bookTitle Here",
"filename": "book file name"
},
"course": [
{
"1": "01",
"2": "02"
},
{
"001": "001",
"002": "002"
}
],
"publisher": "you"
}
**]**
This might solve your problem :)
you can use javascriptserializer
string s = "YouJsonText";
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize(s);
//or
YouCustomClass res = serializer.Deserialize<YouCustomClass>(sb.ToString());
Also, you can use CustomJsonConverter like this:
public class YouCustomClassConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
//and first you need register type, which you want Deserialize
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(YouCustomClass ) }; }
}
}
//and then example of using JavaScriptSerializer with custom converter
var ser = new JavaScriptSerializer();
ser.RegisterConverters(new JavaScriptConverter[] { new YouCustomClassConverter() });
try
{
YouCustomClass obj = ser.Deserialize(jsonString);
}
Note: you need use using System.Web.Script.Serialization;
I'm trying to parse some results from a web service in C# visual studio 2010 that are coming back in JSON but the field named data is sometime a string and sometimes an object. I'm not sure how to get the data contracts to handle that. When it runs through the deserialization it just returns a null result for the everything. If i try a web request that doesn't have any profile_results then works OK.
The JSON looks like this
{
"ListSingleOrganization": {
"id": 86270,
"short_name": "someshort name",
"long_name": "Long name",
"created_at": "2014-05-02T14:21:06Z",
"is_disabled": false,
"description": "Verbose description",
"renewed_at": "2014-05-02T14:21:07Z",
"alternate_id": null,
"website_url": "http://www.url.com",
"member_count": 50,
"pic_url": "https://www.somepicture.com/1.jpg",
"umbrella_id": 36016,
"keywords": "Fraternity, Leadership, Human Service, Scholarship",
"category": {
"id": 53282,
"name": "Fraternities"
},
"profile_responses": [
{
"element": {
"id": 51350,
"name": "Group Email Address",
"type": "Email"
},
"data": "groupsemail#gmail.com"
},
{
"element": {
"id": 1239634,
"name": "Please list your organization's Twitter handle so we may follow you with our office's account. (#something)",
"type": "TextField"
},
"data": "#handle"
},
{
"element": {
"id": 1192652,
"name": "Is this a new organization?",
"type": "Radio"
},
"data": {
"id": 2003570,
"name": "No"
}
}
]
}
}
the difference is based on the element type but i don't know how to account for that with them having the same name.
right now i have
[DataContract]
class ListSingleOrganization
{
//other members
[DataMember(Name = "profile_responses")]
public List<profile_responses> profile_responses { get; set; }
}
[DataContract]
class profile_responses
{
[DataMember(Name = "element")]
public element element { get; set; }
[DataMember(Name="data")]
public data data {get; set; }
}
[DataContract]
class element
{
[DataMember(Name = "id")]
public int id { get; set; }
[DataMember(Name = "name")]
public string name { get; set; }
[DataMember(Name = "type")]
public string type { get; set; }
}
[DataContract]
class data
{
[DataMember(Order=1)]
public string raw { get; set; }
[DataMember(Name = "file_name")]
public string file_name { get; set; }
[DataMember(Name = "url")]
public string url { get; set; }
[DataMember(Name = "id")]
public int id { get; set; }
[DataMember(Name = "name")]
public string name { get; set; }
}
You can create a custom formatter to read the property, check if it a string. If it's a string then create a new instance of your data class and set one of the properties to the string value. Otherwise, deserialize it to a your data object.
public class JsonDataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(data));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
data returnVal = null;
// You may need to debug this and see which token type is being returned
// per data element and adjust, but the principle stands.
if (reader.TokenType == JsonToken.String)
{
returnVal = new data();
returnVal.raw = reader.ReadAsString();
}
else
{
returnVal = serializer.Deserialize<data>(reader);
}
return returnVal;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Assuming you only need to deserialize
throw new NotImplementedException();
}
}
Associate the JsonConverter with your property:
[DataMember(Name = "data")]
[JsonConverter(typeof(JsonDataConverter))]
public data data { get; set; }
EDIT: (answering follow up question)
Set this up in an isolated unit test and get it to work there first before trying to put this in a SSIS package. If you are using the DataContractSerializer then the JsonDataConverter class will not be invoked. You want to deserialize using Json.NET:
using Newtonsoft.Json;
. . .
var orgList JsonConvert.DeserializeObject<ListSingleOrganization>(webServiceResultString);
Couldn't get the DataContract Serializes to work with these irregularities so I went the dynamic object route and that worked.
Deserialize JSON into C# dynamic object?