FormUrlEncoded deprecated in RetroFit? - retrofit

I found that in 2.0.0-SNAPSHOT version the FormUrlEncoded annotation is no longer supported:
} else if (annotationType == FormUrlEncoded.class) {
if (requestType != RequestType.SIMPLE) {
throw methodError("Only one encoding annotation is allowed.");
}
throw new UnsupportedOperationException("Form URL encoding shall return!");
//requestType = RequestType.FORM_URL_ENCODED;
}
Still, when I remove the annotation I got another exception:
Exception in thread "main" java.lang.IllegalArgumentException: LoginService.login: #Field parameters can only be used with form encoding. (parameter #1)
Why is so and why the FormUrlEncoded annotation itself has not been annotated with #Deparcated annotation?

Related

Why does plainToClass not throw for invalid input types

Example:
class Test {
#IsString()
readonly table!: string;
}
// note, that we call it with a completely wrong type: string instead of JSON-object
const result = plainToClass(Test, 'not a class');
Then result is the input string 'not a class'!
I'd expect plainToClass to:
return an instance of type Test where all properties remain undefined, or
throw an exception
Is this a bug or am I missing something?
I also found not option to make this work as expected.
The workround for now is to explicitly typecheck and throw in my own code:
if (!(result instanceof Test) throw ...;
using class-transformer 0.2.3

Unexpected error when deserializing unknown types with Json.Net

In the following code, I serialize an object using Json.Net. This Json has type names embedded. I then change one of the type names to induce an error (this is a test, I am dealing with a real issue in an existing project). When I deserialize the Json, I expect to get an object back that has a null value for the property with the fiddled type name. Instead the serializer craps out and returns null. Are my expectations correct? Can I change the settings somehow so that I will get a non-null object for my root object? Note that the second error that I get suggests that there is a bug in the serializer.
static public class JsonTest
{
static public void Test()
{
// Create test object
A a = new A
{
MyTest = new MyTest(),
};
// Serialize it.
string json = JsonConvert.SerializeObject(a, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
// Fiddle class name to induce error
json = json.Replace("+MyTest", "+MyTest2");
// Before: {"MyTest":{"$type":"<Namespace>.JsonTest+MyTest, <Assembly>"}}
// After: {"MyTest":{"$type":"<Namespace>.JsonTest+MyTest2, <Assembly>"}}
// Deserialize
A a2 = JsonConvert.DeserializeObject<A>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Error = (object sender, ErrorEventArgs e) =>
{
e.ErrorContext.Handled = true; // Should have only one error: the unrecognized Type
}
});
// A second error occurs: Error = {Newtonsoft.Json.JsonSerializationException: Additional text found in JSON string after finishing deserializing object....
// a2 is null
}
public class A
{
public ITest MyTest { get; set; }
}
public interface ITest { }
public class MyTest : ITest { }
}
Update
This issue has been fixed in Json.NET 10.0.2 in this submission.
Original Answer
This looks to be a bug in Json.NET. If I set JsonSerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead then the problem goes away:
// Deserialize
A a2 = JsonConvert.DeserializeObject<A>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead,
Error = (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs e) =>
{
Debug.WriteLine(e.ErrorContext.Path);
e.ErrorContext.Handled = true; // Should have only one error: the unrecognized Type
}
});
Debug.Assert(a2 != null); // No assert.
However, it should not be necessary to turn on this setting, which enables reading metadata properties including "$type" located anywhere in a JSON object, rather than just as the first property. Most likely it coincidentally fixes the bug since it requires pre-loading the entire JSON object before beginning deserialization.
You could report an issue if you want.
Debugging a bit, the problem seems to be that, because the inner MyTest object cannot be constructed, the exception is caught and handled by JsonSerializerInternalReader.PopulateObject() while populating the outer object A. Because of this, the JsonReader does not get advanced past the inner, nested object, leaving the reader and serializer in an inconsistent state. This accounts for the second exception and the eventual Additional text found in JSON string after finishing deserializing object. Path '' exception.

Custom ExceptionHandler for HttpMessageNotReadableException in Spring Data REST

When POSTing an invalid JSON to a Spring Data REST (Version 2.2.2) Resource, an HttpMessageNotReadableException is thrown and the following json is returned:
{
"cause": {
"cause": {
"cause": null,
"message": "Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value\n at [Source: org.apache.catalina.connector.CoyoteInputStream#646d9de6; line: 2, column: 20]"
},
"message": "Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value\n at [Source: org.apache.catalina.connector.CoyoteInputStream#646d9de6; line: 2, column: 20] (through reference chain: gr.bytecode.exceptionhandling.domain.Book[\"name\"])"
},
"message": "Could not read JSON: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value\n at [Source: org.apache.catalina.connector.CoyoteInputStream#646d9de6; line: 2, column: 20] (through reference chain: gr.bytecode.exceptionhandling.domain.Book[\"name\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value\n at [Source: org.apache.catalina.connector.CoyoteInputStream#646d9de6; line: 2, column: 20] (through reference chain: gr.bytecode.exceptionhandling.domain.Book[\"name\"])"
}
Is there a sensible way to override this behavior and return a custom error message?
Here's what I've tried so far:
I added a custom #ExceptionHandler for that Exception but it never gets invoked:
#ControllerAdvice
public class CustomResponseEntityExceptionHandler {
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status) {
ErrorMessage errorMessage = new ErrorMessage("some error");
return new ResponseEntity<Object>(errorMessage, headers, status);
}
}
ErrorMessage is a simple POJO.
It seems that the AbstractRepositoryRestController of the Spring Data REST, at line #97 defines a method that has precedence:
#ExceptionHandler({ HttpMessageNotReadableException.class })
#ResponseBody
public ResponseEntity<ExceptionMessage> handleNotReadable(HttpMessageNotReadableException e) {
return badRequest(e);
}
I also tried to extend ResponseEntityExceptionHandler but similarly, it is also not advised.
Using: Spring Data REST 2.2.2 and Spring Boot 1.2.2
ta.
Update: Demo maven project available on github
One way to handle this would be, to extend the CustomResponseEntityExceptionHandler with ResponseEntityExceptionHandler as you did with one change:
Instead of returning ResponseEntityyou can call handleExceptionInternal method that is provided by ResponseEntityExceptionHandler. This method along with several other parameters, also takes in a body parameter. The body parameter can be set with your custom Error object.
For example:
ErrorInfo er = new ErrorInfo(request.getDescription(false), ex.getLocalizedMessage());
return handleExceptionInternal(ex, er, headers, status, request);

How does JSON.NET serialize objects when using EF table-per-hierarchy?

I'm using Entity Framework Code First table-per-hierarchy in my ASP.NET Web API project. One of my models has a List that is of the type of the abstract base class of the hierarchy.
List<Dog> // (for example; with GoldenRetriever, BorderCollie, etc inheriting)
I'm trying to test POSTing some data to my API Controller using Fiddler. But I don't know how to represent the JSON when I do so. If I try something like:
"Dogs":
[
{"Name":"Bud", "Age":"3"}
]
I get the error:
"Could not create an instance of type Models.Dog. Type is an interface
or abstract class and cannot be instantiated."
Specifying the Discriminator in the JSON doesn't help me either. Anyone have any ideas? Thanks!
Edit: Solution
The trick is to use the $type property in the JSON string. For more information see this link suggested by m.t.bennett in the comments.
To enable using the $type property I needed to add the following to WebApiConfig.cs:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling
= TypeNameHandling.Auto;
Then when posting the JSON to Fiddler, I added the $type property with the full object path:
{"$type":"Example.Models.Dogs.GoldenRetriever, Example.Models",
"Name":"Bud","Age":3}
For me to figure out this formatting I used Snixtor's suggestion to serialize an object and output the JSON string. Brilliant!
I'm not sure if this is the most elegant solution since it's JSON.NET specific, but it works!
I used a custom JsonConverter to handle the base-type deserialization.
public override bool CanConvert(Type objectType) {
return typeof(Mybase).Equals(objectType);
}
public override MyBase Deserialize(JsonReader reader, MyBase existingValue, JsonSerializer serializer) {
var ret = existingValue;
var jobj = JObject.ReadFrom(reader);
if (jobj["type"] == null || jobj["type"].Type != JTokenType.String)
throw new JsonSerializationException("Supplied JSON is missing the required 'type' member.");
var typeName = jobj["type"].Value<string>();
var t = this.GetType().Assembly.GetType("MyNamespace." + typeName);
if(t == null)
throw new JsonSerializationException(String.Format("Could not identify supplied type '{0}' as a known type.", typeName));
if (existingValue != null && !t.IsInstanceOfType(existingValue))
throw new JsonSerializationException(String.Format("Type Mismatch: existingValue {0} is not assignable from supplied Type {1}", existingValue.GetType().Name, t.Name));
ret = (ContactMethod)jobj.ToObject(t);
return ret;
}
And registered the JsonConverter durring application initialization:
JsonSerializerSettings settings;
settings.Converters.Add(new MyBaseConverter());

Object reference not set to an instance of an object in Session

I am getting an error in this line of code Session.Linq<Employees>() :
" An object reference is required for non-static field,method, or property 'System.Web.UI.Page.Session.get'.
This is my code :
public static object GetData(Dictionary<string, object> tableParams)
{
IQueryable<Employees> Employee = Session.Linq<Employees>();
if (tableParams.ContainsKey("sEcho"))
{
var parser = new DataTableParser<Employees>(tableParams, Employee);
return parser.Parse();
}
return Employee;
}
If I use HttpContext.Current.Session.Linq<Employees>();
then i get:
'System.Web.SessionState.HttpSessionState' does not contain a definition for 'Linq' and no extension method 'Linq' accepting a first argument of type 'System.Web.SessionState.HttpSessionState' could be found '
What do i need to do to get this to work? Am I missing a namespace for Linq with regard to Session?I am using System.Linq and System.Linq.Expression.
I think you're misunderstanding something. What you're trying to do doesn't have anything to do with Linq, at least not in the context of retrieving the object from session.
You need to retrieve the object from session and unbox it:
var list = Session["MyList"] as List<int>;
if (list != null)
{
//the list was found and you can start using it here
}

Resources