I get an InvalidCastException converting a linq entity list to a businessobject list using the .Cast<> operator.
"Unable to cast object of type 'Ticketing.ticket' to type
'Ticketing.ModelTicket'." (namespace name was changed because underscore was causing unneeded formatting)
here's my business object class
public sealed class ModelTicket
{
public ModelTicket(ticket ticket)
{
_Ticket = ticket;
}
public static implicit operator ModelTicket(ticket item)
{
return new ModelTicket(item);
}
}
and here's my extension method to convert a list of linq objects to a list of business objects:
public static class ModelTicketExtensions
{
public static List<ModelTicket> ToModelTickets(this List<ticket> list)
{
return list.Cast<ModelTicket>().ToList();// exception on cast
}
}
I would go with the following function:
public static class ModelTicketExtensions
{
public static List<ModelTicket> ToModelTickets(this List<ticket> list)
{
return list.ConvertAll<ModelTicket>(t => (ModelTicket)t);
}
}
If that doesn't work for you, then you can go the completely direct route:
public static class ModelTicketExtensions
{
public static List<ModelTicket> ToModelTickets(this List<ticket> list)
{
return list.ConvertAll<ModelTicket>(t => new ModelTicket(t));
}
}
I'd say the second is arguable more clear on exactly what is happening.
Related
I have an enum defined as:
public static enum State {
#JsonProperty("At Rest")
AT_REST,
#JsonProperty("In Motion")
IN_MOTION,
#JsonProperty("Stalled")
STALLED;
}
So, the server produces "At Rest" when Jackson serializes the AT_REST enum into JSON. Similarly, Jackson deserializes "At Rest" into AT_REST when the client passes JSON to the server. For example:
#GetMapping()
public State[] getAllStates() {
return State.values(); //returns JSON ["At Rest", "In Motion", "Stalled"]
}
#PostMapping()
public void saveState(#ResponseBody State state /*when client sends "At Rest", it gets converted into Enum*/) {
//save state
}
I also have a search GET endpoint. The client calls it with a "state" query parameter such https://localhost/search?state=At Rest. Since the query parameter value is not JSON, I have a Spring converter:
#Component
public class StringToStateConverter implements Converter<String, State> {
#Override
public State convert(String description) {
if ("At Rest".equals(description)) {
return State.AT_REST;
} else if ("In Motion".equals(description)) {
return State.IN_MOTION;
} else if ("Stalled".equals(description)) {
return State.STALLED;
} else {
return null;
}
}
}
Is it possible to have Spring use JsonProperty when deserializing a query param? If not, how can I avoid having the String description in multiple places in my code? I prefer not to make a description field in the enum since this is really just for client display.
Is it possible to have Spring use JsonProperty when deserializing a query param?
Yes.
#Component
#RequiredArgsConstructor
public class StringToStateConverter implements Converter<String, State> {
private final ObjectMapper mapper;
#Override
public State convert(String description) {
try {
return mapper.readValue("\"" + description + "\"", State.class);
} catch (JsonProcessingException e) {
// code to return error to client
}
}
I have a sample response class that extends RepresentationModel. In some scenarios I don't add any hateoas links in the response. In that case, I'm getting an empty links field in the json response
"links": []
I tried adding "JsonInclude.Include.NON_EMPTY" to the response class, but since the links field is final in RepresentationModel, it's still bringing empty links field in the response.
How can I avoid this empty links field in the response ?
Firstly make sure you have a good reason to use links with media type application/json rather than media type built for hypermedia such as HAL (application/hal+json).
Though RepresentationModel has a field of List<Link>, the getter returns a Links instead of List<Link>. Jackson treats it as simple type (where a JsonSerializer is used) instead of a collection type (where a CollectionSerializer is used), so JsonInclude.Include.NON_EMPTY doesn't work as you expect.
public class RepresentationModel<T extends RepresentationModel<? extends T>> {
private final List<Link> links;
#JsonProperty("links")
public Links getLinks() {
return Links.of(links);
}
}
public class Links implements Iterable<Link> { }
public abstract class JsonSerializer<T> {
public boolean isEmpty(SerializerProvider provider, T value) {
return (value == null);
}
}
public class CollectionSerializer {
#Override
public boolean isEmpty(SerializerProvider prov, Collection<?> value) {
return value.isEmpty();
}
}
One solution is override the getter getLinks() and use a customm filter.
class User extends RepresentationModel<User> {
// ...
#JsonProperty("links")
// if links is an empty JSON array, exclude it
#JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = EmptyLinksFilter.class)
#Override
public Links getLinks() {
return super.getLinks();
}
}
/* The word "filter" is a bit ambiguous (included? or excluded?).
Here when the equals() of this class return true, the value will be excluded.
Choose a class name to make yourself comfortable. */
class EmptyLinksFilter{
#Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Links)) {
return false;
}
Links links = (Links) obj;
return links.isEmpty();
}
}
The full code is in Github.
Second solution may be custom mixin like what Spring HATEOAS already build for HAL. Related code are:
RepresentationModelMixin
Jackson2HalModule.HalLinkListSerializer
Jackson2HalModule
HalMediaTypeConfiguration
The second solution is much complicated. That's why I recommand media types like HAL, for which Spring HATEOAS already has good configuration.
As per the answer from #yejianfengblue, I have created a custom representation model as below and extended this CustomRepresentationModel from response java classes instead of Hateoas RepresentationModel.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.lang.NonNull;
public class CustomRepresentationModel<T extends CustomRepresentationModel<? extends T>> extends
RepresentationModel<T> {
#JsonProperty("_links")
#JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = NonEmptyLinksFilter.class)
#NonNull
#Override
public Links getLinks() {
return super.getLinks();
}
static class NonEmptyLinksFilter {
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Links)) {
return false;
}
Links links = (Links) obj;
return links.isEmpty();
}
#Override
public int hashCode() {
return super.hashCode();
}
}
}
I'm currently working on a project where I need to fetch a large amount of data from the Database and parse it into a specific Json format, I already have built my custom Serializers and Its working properly when i pass a List to Gson. But as I was already working with Streams from my JPA Layer, I thought I could pass the Stream down to the Gson parser so that it could transform it directly to my Json data. But I'm getting an empty Json object instead of a correctly populated one.
So, if anyone could point to me a way to make Gson work with Java 8 Streams or if this isn't possible currently.. i could not find anything on Google, so i came to Stackoverflow.
You could use JsonWriter to streaming your data to output stream:
public void writeJsonStream(OutputStream out, Stream<DataObject> data) throws IOException {
try(JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"))) {
writer.setIndent(" ");
writer.beginArray();
data.forEach(d -> {
d.beginObject();
d.name("yourField").value(d.getYourField());
....
d.endObject();
});
writer.endArray();
}
}
Note that you're in charge of controling the json structure.
That is, if your DataObject contains nested Object, you have to write beginObject()/endObject() respectively. The same goes for nested array.
It is not as trivial as one would expect, but it can be done in a generic way.
When you look into the Javadoc to TypeAdapterFactory, they provide a very simplistic way of writing a TypeAdapterFactory for a custom type. Alas, it does not work as expected because of problems with element type detection. The proper way to do this can be found in Gson-internal CollectionTypeAdapterFactory. It is quite complex, but taking what's necessary one can come up with something like that:
final class StreamTypeAdapterFactory implements TypeAdapterFactory {
#SuppressWarnings("unchecked")
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!Stream.class.isAssignableFrom(rawType)) {
return null;
}
Type elementType = ExtraGsonTypes.getStreamElementType(type, rawType);
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
return (TypeAdapter<T>) new StreamTypeAdapter<>(elementAdapter);
}
private static class StreamTypeAdapter<E> extends TypeAdapter<Stream<E>> {
private final TypeAdapter<E> elementAdapter;
StreamTypeAdapter(TypeAdapter<E> elementAdapter) {
this.elementAdapter = elementAdapter;
}
public void write(JsonWriter out, Stream<E> value) throws IOException {
out.beginArray();
for (E element : iterable(value)) {
elementAdapter.write(out, element);
}
out.endArray();
}
public Stream<E> read(JsonReader in) throws IOException {
Stream.Builder<E> builder = Stream.builder();
in.beginArray();
while (in.hasNext()) {
builder.add(elementAdapter.read(in));
}
in.endArray();
return builder.build();
}
}
private static <T> Iterable<T> iterable(Stream<T> stream) {
return stream::iterator;
}
}
The ExtraGsonTypes is a special class that I used to circumvent package-private access to $Gson$Types.getSupertype method. It's a hack that works if you're not using JDK 9's modules - you simply place this class in the same package as $Gson$Types:
package com.google.gson.internal;
import java.lang.reflect.*;
import java.util.stream.Stream;
public final class ExtraGsonTypes {
public static Type getStreamElementType(Type context, Class<?> contextRawType) {
return getContainerElementType(context, contextRawType, Stream.class);
}
private static Type getContainerElementType(Type context, Class<?> contextRawType, Class<?> containerSupertype) {
Type containerType = $Gson$Types.getSupertype(context, contextRawType, containerSupertype);
if (containerType instanceof WildcardType) {
containerType = ((WildcardType)containerType).getUpperBounds()[0];
}
if (containerType instanceof ParameterizedType) {
return ((ParameterizedType) containerType).getActualTypeArguments()[0];
}
return Object.class;
}
}
(I filed an issue about that in GitHub)
You use it in the following way:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new StreamTypeAdapterFactory())
.create();
System.out.println(gson.toJson(Stream.of(1, 2, 3)));
In order to find walk-around for absence RelativeSource in MvvmCross, I used Stuart's suggestion and implemented WrappingList
MVVMCross changing ViewModel within a MvxBindableListView
However, I see this trace every bind it happens and I wonder, how worse it is:
Binding to IEnumerable rather than IList - this can be inefficient,
especially for large lists
Maybe there are any other suggestions?
public class WrappingCommandsList<T> : IList<WrappingCommandsList<T>.Wrapped>
{
private readonly List<T> _realList;
private readonly Action<T> _realActionOnClick;
public class Wrapped
{
public IMvxCommand ClickCommand { get; set; }
public T TheItem { get; set; }
}
public WrappingCommandsList(List<T> realList, Action<T> realActionOnClick)
{
_realList = realList;
_realActionOnClick = realActionOnClick;
}
private Wrapped Wrap(T item)
{
return new Wrapped()
{
ClickCommand = new MvxCommand(() => _realActionOnClick(item)),
TheItem = item
};
}
public WrappingCommandsList<T>.Wrapped this[int index]
{
get
{
return Wrap(_realList[index]);
}
set
{
throw new NotImplementedException();
}
}
public int Count
{
get { return _realList.Count; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<WrappingCommandsList<T>.Wrapped> GetEnumerator()
{
foreach (var item in _realList)
{
yield return Wrap(item);
}
}
How inefficient depends on the length of your list and how far down it the used has scrolled.
eg if the user is showing item 93,256 on the screen then the only way for the list adapter to find item 93,256 is to get the enumerator and to call MoveNext 93,256 times.
Whereas if your list only has 5 items the problem is bounded by 5.
For your particular WrappingCommandsList try implementing IList as well as IList<T> - the mvx code can't generate the IList<T> accessors at runtime because of xamarin.ios AoT compilation restrictions.
I have the following code.
public class SomeClass {
private List<SomeOtherClass> referrals;
public List<SomeOtherClass> getReferrals() {
return referrals;
}
public void setReferrals( List<SomeOtherClass> referrals) {
this.referrals = referrals;
}
}
I have a json that I read from the wire. It is correctly formatted. I use GSON
My question is :
when I do fromJson(jsonString,SomeClass.class);
it gives an exception.
If I don't use List<SomeOtherClass> but instead use List<String> for referrals.
(in other word a primitive)
And iterate over each String and create SomeOtherClass object it works fine.
Why can't I just use fromJson(jsonString,SomeClass.class);