Why does AutoMapper throw an InvalidCastException when mapping this getter-only property? - automapper-11

From what I've read, AutoMapper is supposed to ignore getter-only properties. But with the configuration below the mapper throws an InvalidCastException due to this getter-only property
using AutoMapper;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Model1, Model2>();
});
var mapper = new Mapper(config);
var m1 = new Model1
{
StringList = new List<string> { "String" }
};
var m2 = mapper.Map<Model2>(m1);
public class Model1
{
public List<string> StringList { get; set; }
public IEnumerable<object> Objects => StringList;
}
public class Model2
{
public List<string> StringList { get; set; }
public IEnumerable<object> Objects => StringList;
}
The Map() line throws this exception
AutoMapper.AutoMapperMappingException: 'Error mapping types.'
Inner Exception
InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[System.String]' to type 'System.Collections.Generic.ICollection`1[System.Object]'.

That doesn't apply to collections because they don't need a setter to be mapped, you can simply map into that collection.
See https://docs.automapper.org/en/latest/10.0-Upgrade-Guide.html#all-collections-are-mapped-by-default-even-if-they-have-no-setter

Related

Asp .Net Core Json deserialize to model

Returns Json format data to me from an api.
However, the type of the "FuelType" property inside the object may be different. For example, 1 time comes as follows:
{
...
fuelType: "gasoline"
...
}}
But then it can happen:
{
...
fuelType: ["gasoline", "any"]
...
}}
If I set the "FuelType" property type on my model to a string, in the second case, Json will give me an error when it arrives, because it can't convert from array to string. No, if I set the type to an array, then, conversely, if a string arrives, it will issue an error because it cannot convert from a string to an array.
In this case, what should I put the "FuelType" property type in my model so that it does not make an error when deserializing?
It all depends on you,what type of value you want to receive?string or List?
I tried with the codes:
public class SomeModel
{
public SomeModel()
{
fuelType = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<string> fuelType { get; set; }
//you could move the codes to someservice
public List<string> someresult(string a)
{
var targetlist = new List<string>();
targetlist.Add(a);
return targetlist;
}
public List<string> someresult(List<string> a)
{
var targetlist = new List<string>();
targetlist.AddRange(a);
return targetlist;
}
}
[HttpPost]
public JsonResult SomeAction()
{
var somemodel = new SomeModel() { Id = 1, Name = "name" };
var somevalue = /*"a"*/new List<string>() { "a", "b", "c" };
var targetvalue = somemodel.someresult(somevalue);
somemodel.fuelType.AddRange(targetvalue);
return new JsonResult(somemodel);
}
Result:
try this
var fuelType = JsonConvert.DeserializeObject<MyClass>(json);
public class MyClass
{
[JsonProperty("fuelType")]
private JToken _fuelType;
[JsonIgnore]
public string[] fuelType
{
get {
if (_fuelType==null) return null;
return _fuelType is JArray ? _fuelType.ToObject<string[]>() : new string[] { (string)_fuelType }; }
set { _fuelType = JsonConvert.SerializeObject(value); }
}
}

Custom Json Converter With Constructor Arguments

I am trying to create a custom Json converter that has no default constructor and instead takes a factory that is dependency injected by Autofac. When ever I hit the object that uses this converter I get an exception that there is no no-arg constructor to use for the deserialization.
I have an objects and primitives. One of the objects is an abstract base object that I have the converter on. Since this converter is abstract I want to dependency inject a factory into the converter's ReadJson method to make the choice as to what conversion to make.
Currently the code is something like the following:
using System;
using System.Collections.Generic;
using Autofac;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Newtonsoft = Newtonsoft.Json;
public class JsonModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<SubThingFactory>()
.As<IFactory>()
.SingleInstance();
builder.Register(c => this.CreateJsonSerializerSettings(c)).SingleInstance();
builder.RegisterType<CamelCasePropertyNamesContractResolver>()
.As<IContractResolver>()
.SingleInstance();
builder.RegisterType<IsoDateTimeConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.RegisterType<SubThingConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.Register(c => new StringEnumConverter
{
CamelCaseText = true
})
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
}
private Newtonsoft.JsonSerializerSettings CreateJsonSerializerSettings(IComponentContext context)
{
var settings = new Newtonsoft.JsonSerializerSettings
{
DefaultValueHandling = Newtonsoft.DefaultValueHandling.Ignore,
NullValueHandling = Newtonsoft.NullValueHandling.Ignore,
DateTimeZoneHandling = Newtonsoft.DateTimeZoneHandling.Utc
};
settings.ContractResolver = context.Resolve<IContractResolver>();
foreach (var converter in context.Resolve<IEnumerable<Newtonsoft.JsonConverter>>())
{
settings.Converters.Add(converter);
}
return settings;
}
}
public class ThingBeingDeserialized
{
private string Name;
private SubThing subby;
}
[Newtonsoft.JsonConverterAttribute(typeof(SubThingConverter))]
public abstract class SubThing
{
public string Name { get; set; }
public virtual string GetName()
{
//Uses reflection to get the name from a custom attribute
return this.Name;
}
}
[CustomName("A")]
public class SubThingA : SubThing
{
public int Field1 { get; set; }
}
[CustomName("B")]
public class SubThingB : SubThing
{
public string Field2 { get; set; }
}
public class SubThingConverter : Newtonsoft.JsonConverter
{
//This is Autofac injected in
private readonly IFactory factory;
public SubThingConverter(IFactory factory)
{
this.factory = factory;
}
public override object ReadJson(Newtonsoft.JsonReader reader, Type objectType, object existingValue, Newtonsoft.JsonSerializer serializer)
{
if (reader.TokenType == Newtonsoft.JsonToken.Null)
{
return null;
}
var jsonObject = JObject.Load(reader);
var type = jsonObject["type"].ToString();
return this.factory.GetSubThing(type, jsonObject);
}
public override void WriteJson(Newtonsoft.JsonWriter writer, object value, Newtonsoft.JsonSerializer serializer)
{
var type = value.GetType();
var properties = type.GetProperties();
var jObject = new JObject
{
{ "type", type.Name }
};
foreach (var prop in properties)
{
if (prop.CanRead)
{
var propVal = prop.GetValue(value, null);
if (propVal != null)
{
jObject.Add(prop.Name, JToken.FromObject(propVal, serializer));
}
}
}
jObject.WriteTo(writer);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SubThing);
}
}
public interface IFactory
{
SubThing GetSubThing(string type, JObject restOfObj);
}
public class SubThingFactory : IFactory
{
public SubThing GetSubThing(string type, JObject restOfObj)
{
switch (type)
{
case "A":
return new SubThingA
{
Field1 = (int)(restOfObj["Field1"])
};
case "B":
return new SubThingB
{
Field2 = (string)(restOfObj["Field2"])
};
}
return null;
}
}
public class CustomNameAttribute : Attribute
{
public CustomNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
The way I am doing the Autofac injection for the JsonSerializerSettings is by registering the settings such that the settings.Converters will pick up the enumeration of all the JsonConverters that are registered with the Autofac container and the SubThingConverter is registered such that when it is resolved it will have the IFactory resolved for it and the JsonSerializer also comes from the autofac container with these settings.
Even when I skip the dependency injection and use a new JsonSerializer with the JsonSerializerSettings with the custom converter added as
settings.Converters.Add(new SubThingConverter(new SubThingFactory()))
I still get the complaint that the SubThingConverter does not have a no arg constructor.
It seems to me that overridding the settings to explicitly use this converter should be enough. I also tried adding in the object[] params in the JsonConverter attribute on the SubThing, I couldn't get it to work and it seems to need to be a compile time array, which doesn't work with the dependency injection I need to do. Any pointers would be greatly appreciated. Thank you!

Deserialise dynamic json types

I want to deserialize json which returns different data for different parameters.
Mostly I get:
{"posts": [{ "id" : 1, "name":"post1" },{ "id" : 1, "name":"post1" }]}
But sometimes the data returned is
{"posts": false}
I want to deserialize this as the following class
public class GetReaderResponse
{
public IEnumerable<ReaderPost> posts {get; set;}
}
public class ReaderPost
{
public int id {get; set;}
public string name{get; set;}
}
I am using C#,json.net but not able to do this correctly.
Newtonsoft.Json.JsonConvert.DeserializeObject<GetReaderResponse>(dataString);
You could build a custom converter, but an easy way to handle this would be to write an error handler that detects errors with the posts property:
var settings = new JsonSerializerSettings();
settings.Error += (sender, args) =>
{
if (string.Equals("posts", args.ErrorContext.Path, StringComparison.OrdinalIgnoreCase))
{
var currentObject = args.CurrentObject as GetReaderResponse;
currentObject.posts = Enumerable.Empty<ReaderPost>();
args.ErrorContext.Handled = true;
}
};
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
This sets posts to Enumerable.Empty<ReaderPost>. This is still a little unsatisfying because if any error occurs, the property will be set. You could build a full custom converter to do this as a more complete solution.
Here's a converter that will take care of this:
public class PostsConverter : JsonConverter
{
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JToken val = JValue.ReadFrom(reader);
object result = null;
if (val.Type == JTokenType.Array)
{
result = val.ToObject<IEnumerable<ReaderPost>>();
}
else if (val.Type == JTokenType.Boolean)
{
result = Enumerable.Empty<ReaderPost>();
}
return result;
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert (Type type)
{
return typeof(IEnumerable<ReaderPost>).IsAssignableFrom(type);
}
public override bool CanRead
{
get { return true; }
}
}
Usage:
var settings = new JsonSerializerSettings();
settings.Converters = new [] { new PostsConverter() };
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
Example: https://dotnetfiddle.net/i9CXwp
By using JSON.NETs built in LINQ to JSON, you can try someting like this:
JObject jObject = JObject.Parse(json);
GetReaderResponse response = new GetReaderResponse();
if (jObject["posts"] is JArray)
response = jObject.ToObject<GetReaderResponse>();
// Do something with the response object.
where json variable is the json string you need to deserialize.
try this:
public class GetReaderResponse
{
public bool posts { get; set; }
public ReaderPost[] post { get; set; }
}
After reading #Ilija's comment I think I might have found a answer. I did not want not use string literals so I modified my class GetReaderResponse to look like below:
public class GetReaderResponse
{
public dynamic posts {get; set;}
public IEnumerable<ReaderPost> Posts
{
get
{
if (posts is bool )
return new ReaderPost[0];
return posts.ToObject<IEnumerable<ReaderPost>>();
}
}
}
Does this sound fine or does it look messy?

Jersey client read json response into custom object

public class RESTDataServiceClient{
private Client client;
private String dataServiceUri;
private String dataServiceResource;
private CustomData customData;
public RESTDataServiceClient(String dataServiceUri, String dataServiceResource, Client client){
this.client = client;
this.dataServiceUri = dataServiceUri;
this.dataServiceResource = dataServiceResource;
}
#Override
public CustomData getCustomData() {
WebTarget dataServiceTarget = client.target(dataServiceUri).path(dataServiceResource);
Invocation.Builder invocationBuilder = dataServiceTarget.request(MediaType.APPLICATION_JSON_TYPE);
Response response = invocationBuilder.get();
myCustomData = response.readEntity(CustomData.class);
return myCustomData;
}
}
CustomData.java
public class CustomData{
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
//getter and setter
}
sample json content
{"50000":{"testString":{"1":10}},"50001":{"testString1":{"2":11}} }
I am trying to get data from a data service which is going to return data in a JSON format. I am trying to write a client to read that JSON into a custom object. The CustomData contains a nested trove map datastructure. we wrote a custom serializer for that and the server part works fine. I am unable to get the rest client read the data into an object, but reading into string works. I tried above pasted code with the sample data and i get the error below.
javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:866)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
at com.sample.data.RESTDataServiceClient.getCustomData(RESTDataServiceClient.java:42)
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "50000" (class com.sample.data.CustomData), not marked as ignorable (0 known properties: ])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#2cb89281; line: 1, column: 14] (through reference chain: com.sample.data.CustomData["50000"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:773)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1275)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1233)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:677)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:777)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:264)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:234)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
... 38 more
TLongObjectMap is not deserializable out of the box, so how you made a custom serializer you also need to implement a custom deserializer. You can package these up nicely in a module and add it to your ObjectMapper.
It looks like there is a Trove module in development right now, which you can download and add to your ObjectMapper the same as the example below. The TIntObjectMapDeserializer implementation in that link is much more robust then my solution, so I would recommend using that class in your project if possible.
If you want to try and write it yourself, here's a starting point that properly deserializes your provided example:
public class FakeTest {
#Test
public void test() throws Exception {
ObjectMapper om = new ObjectMapper();
om.registerModule(new CustomModule());
String s = "{\"50000\":{\"testString\":{\"1\":10}},\"50001\":{\"testString1\":{\"2\":11}} }";
CustomData cd = om.readValue(s, CustomData.class);
System.out.println(cd.getData());
}
public static class CustomData {
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
public TLongObjectMap<Map<String, TIntIntMap>> getData() { return data; }
public void setData(TLongObjectMap<Map<String, TIntIntMap>> data) { this.data = data; }
}
public static class CustomModule extends SimpleModule {
public CustomModule() {
addSerializer(CustomData.class, new CustomSerializer());
addDeserializer(CustomData.class, new CustomDeserializer());
}
public static class CustomSerializer extends JsonSerializer<CustomData> {
#Override
public void serialize(CustomData value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// add custom serializer here
}
}
public static class CustomDeserializer extends JsonDeserializer<CustomData> {
#Override
public CustomData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TLongObjectMap<Map<String, TIntIntMap>> data = new TLongObjectHashMap<>();
ObjectNode node = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String,JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
ObjectNode value = (ObjectNode) entry.getValue();
Map.Entry<String, JsonNode> innerField = value.fields().next();
ObjectNode innerNode = (ObjectNode) innerField.getValue();
Map.Entry<String, JsonNode> innerInnerField = innerNode.fields().next();
TIntIntMap intMap = new TIntIntHashMap();
intMap.put(Integer.parseInt(innerInnerField.getKey()), innerInnerField.getValue().asInt());
Map<String, TIntIntMap> innerMap = Collections.singletonMap(innerField.getKey(), intMap);
data.put(Long.parseLong(entry.getKey()), innerMap);
}
CustomData customData = new CustomData();
customData.setData(data);
return customData;
}
}
}
}

Unmarshalling JSON array via Jettison/Resteasy

Ran into a similar problem like the following forum post:
http://jersey.576304.n2.nabble.com/parsing-JSON-with-Arrays-using-Jettison-td5732207.html
Using Resteasy 2.0.1GA with Jettison 1.2 and getting a problem marshalling arrays when involving namespace mappings. See code below. Basically if the number of array entries are greater than one and namespace mappings are used. Anybody else run into this problem? The Nabble form poster got around it by writing a custom unmarshaller.
I either need to isolate the Jettison bug or write a Resteasy extension of the JettisonMappedUnmarshaller class (which hands over the namespace mappings and unmarshaller to the Jettison Configuration).
The following code doesn't unmarshall (post step) if the properties variables contains 2 or more entries.
public class Experimenting {
#Path("test")
public static class MyResource {
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Property", propOrder = { "name", "value" })
public static class MyProperty {
#XmlElement(name = "Name", required = true)
protected String name;
#XmlElement(name = "Value", required = true)
protected String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
#XmlType(name = "MyElement", propOrder = { "myProperty" })
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "MyElement", namespace = "http://www.klistret.com/cmdb/ci/commons")
#Mapped(namespaceMap = { #XmlNsMap(namespace = "http://www.klistret.com/cmdb/ci/commons", jsonName = "com.klistret.cmdb.ci.commons") })
public static class MyElement {
#XmlElement(name = "MyProperty", namespace = "http://www.klistret.com/cmdb/ci/commons")
protected List myProperty;
public List getMyProperty() {
if (myProperty == null) {
myProperty = new ArrayList();
}
return this.myProperty;
}
public void setMyProperty(List myProperty) {
this.myProperty = myProperty;
}
}
#GET
#Path("myElement/{id}")
#Produces(MediaType.APPLICATION_JSON)
public MyElement getMy(#PathParam("id")
Long id) {
MyElement myElement = new MyElement();
MyProperty example = new MyProperty();
example.setName("example");
example.setValue("of a property");
MyProperty another = new MyProperty();
another.setName("another");
another.setValue("just a test");
MyProperty[] properties = new MyProperty[] { example, another };
myElement.setMyProperty(Arrays.asList(properties));
return myElement;
}
#POST
#Path("/myElement")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public MyElement createMy(MyElement myElement) {
List properties = myElement.getMyProperty();
System.out.println("Properties size: " + properties.size());
return myElement;
}
}
private Dispatcher dispatcher;
#Before
public void setUp() throws Exception {
// embedded server
dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addPerRequestResource(MyResource.class);
}
#Test
public void getAndCreate() throws URISyntaxException,
UnsupportedEncodingException {
MockHttpRequest getRequest = MockHttpRequest.get("/test/element/44");
MockHttpResponse getResponse = new MockHttpResponse();
dispatcher.invoke(getRequest, getResponse);
String getResponseBodyAsString = getResponse.getContentAsString();
System.out.println(String.format(
"Get Response code [%s] with payload [%s]", getResponse
.getStatus(), getResponse.getContentAsString()));
MockHttpRequest postRequest = MockHttpRequest.post("/test/element");
MockHttpResponse postResponse = new MockHttpResponse();
postRequest.contentType(MediaType.APPLICATION_JSON);
postRequest.content(getResponseBodyAsString.getBytes("UTF-8"));
dispatcher.invoke(postRequest, postResponse);
System.out.println(String.format(
"Post Response code [%s] with payload [%s]", postResponse
.getStatus(), postResponse.getContentAsString()));
}
}
Do you have to use Jettison? If not I would recommend just switching to use Jackson instead; this typically solves array/list related problems (problem with Jettison is that it converts to XML model, which makes it very hard to tell arrays from objects -- there are bugs, too, but it is fundamentally hard thing to get working correctly).