JSON response from WCF escapes name values - json

I am trying to return a dictionary from a WCF REST service as a JSON object. I want the format to be
{"key": "value"}
so I have created my own class as described here and here.
The service method works, sort of. The problem is that the key names are escaped. For example, if my dictionary contains "Hello world": 100, I get
{"Hello_x0020_world":100}. It also escapes other characters like %, etc.
Is there some way I can tell the serialization not to escape the names that way? It is almost like it is using xml rules that don't (necessarily) apply to JSON.
My serializable class:
[Serializable]
public class JsonDictionary : ISerializable
{
private Dictionary<string, object> _Dictionary;
public JsonDictionary()
{
_Dictionary = new Dictionary<string, object>();
}
public JsonDictionary(SerializationInfo info, StreamingContext context)
{
_Dictionary = new Dictionary<string, object>();
SerializationInfoEnumerator enumerator = info.GetEnumerator();
while (enumerator.MoveNext())
{
_Dictionary.Add(enumerator.Name, enumerator.Value);
}
}
public object this[string key]
{
get { return _Dictionary[key]; }
set { _Dictionary[key] = value; }
}
public void Add(string key, object value)
{
_Dictionary.Add(key, value);
}
public bool ContainsKey(string key)
{
return _Dictionary.ContainsKey(key);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (string key in _Dictionary.Keys)
info.AddValue(key, _Dictionary[key], _Dictionary[key] == null ? typeof(object) : _Dictionary[key].GetType());
}
}
My service function definition:
[WebGet(UriTemplate = "", ResponseFormat = WebMessageFormat.Json)]
public JsonDictionary GetCollection()
{
JsonDictionary dict = new JsonDictionary();
dict.Add("Hello world", 100);
return dict;
}

The DataContractJsonSerializer used by default in WCF uses a XML-to-JSON mapping which causes some issues, like the one you see in ISerializable types. You can, however, use a custom formatter to change how the response will be serialized. In the example below, I'm using JSON.NET which deals with ISerializable objects "correctly".
public class StackOverflow_16674152
{
[Serializable]
public class JsonDictionary : ISerializable
{
private Dictionary<string, object> _Dictionary;
public JsonDictionary()
{
_Dictionary = new Dictionary<string, object>();
}
public JsonDictionary(SerializationInfo info, StreamingContext context)
{
_Dictionary = new Dictionary<string, object>();
SerializationInfoEnumerator enumerator = info.GetEnumerator();
while (enumerator.MoveNext())
{
_Dictionary.Add(enumerator.Name, enumerator.Value);
}
}
public object this[string key]
{
get { return _Dictionary[key]; }
set { _Dictionary[key] = value; }
}
public void Add(string key, object value)
{
_Dictionary.Add(key, value);
}
public bool ContainsKey(string key)
{
return _Dictionary.ContainsKey(key);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (string key in _Dictionary.Keys)
info.AddValue(key, _Dictionary[key], _Dictionary[key] == null ? typeof(object) : _Dictionary[key].GetType());
}
}
[ServiceContract]
public class Service
{
[WebGet(UriTemplate = "", ResponseFormat = WebMessageFormat.Json)]
[MyISerializableResponseJsonBehavior]
public JsonDictionary GetCollection()
{
JsonDictionary dict = new JsonDictionary();
dict.Add("Hello world", 100);
return dict;
}
}
public class MyFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter original;
string replyAction;
public MyFormatter(IDispatchMessageFormatter original, string replyAction)
{
this.original = original;
this.replyAction = replyAction;
}
public void DeserializeRequest(Message message, object[] parameters)
{
this.original.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
ISerializable serializable = result as ISerializable;
if (serializable != null)
{
string json = JsonConvert.SerializeObject(serializable);
byte[] bytes = Encoding.UTF8.GetBytes(json);
var writer = new MyRawWriter(bytes);
Message reply = Message.CreateMessage(messageVersion, replyAction, writer);
reply.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return reply;
}
else
{
return this.original.SerializeReply(messageVersion, parameters, result);
}
}
class MyRawWriter : BodyWriter
{
byte[] data;
public MyRawWriter(byte[] data)
: base(true)
{
this.data = data;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Binary");
writer.WriteBase64(data, 0, data.Length);
writer.WriteEndElement();
}
}
}
public class MyISerializableResponseJsonBehaviorAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
if (operationDescription.Messages.Count > 1)
{
dispatchOperation.Formatter = new MyFormatter(dispatchOperation.Formatter, operationDescription.Messages[1].Action);
}
}
public void Validate(OperationDescription operationDescription)
{
if (operationDescription.Messages.Count > 1)
{
var respMessage = operationDescription.Messages[1];
if (respMessage.Body.Parts.Count > 0)
{
throw new InvalidOperationException("Cannot be used with out/ref parameters");
}
}
var wga = operationDescription.Behaviors.Find<WebGetAttribute>();
var wia = operationDescription.Behaviors.Find<WebInvokeAttribute>();
WebMessageBodyStyle bodyStyle = WebMessageBodyStyle.Bare; // default
if (wga != null && wga.IsBodyStyleSetExplicitly) {
bodyStyle = wga.BodyStyle;
}
if (wia != null && wia.IsBodyStyleSetExplicitly) {
bodyStyle = wia.BodyStyle;
}
if (bodyStyle == WebMessageBodyStyle.Wrapped || bodyStyle == WebMessageBodyStyle.WrappedResponse)
{
throw new InvalidOperationException("This behavior can only be used with bare response style");
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/"));
}
}

Related

Passing a method data to other method in same Activity

I am newbie to Android Studio and I am making my final year project.
I made a QR code scanner that can retrieve data from HTTP using Rest API.
My question is: I need to send all the JSON data to other activity, based on my research I need to put intent on my button, because of that I need to pass my JsonRequest data to Btn_BuyClicked method so I can send all those to next activity.
I used AndroidHive MovieTickets so Im not changing so much coding.
Please help me. Thank you.
public class TicketResultActivity extends AppCompatActivity {
private static final String TAG = TicketResultActivity.class.getSimpleName();
private Button btnBuy;
private ImageView imgPoster;
private ProgressBar progressBar;
private TicketView ticketView;
private TextView txtDirector;
private TextView txtYear_created;
private TextView txtError;
private TextView txtType_powder;
private TextView txtApa_number;
private TextView txtLocation;
private TextView txtDate_expired;
private Button signOut;
private FirebaseAuth auth;
private class Movie {
String director;
String year_created;
String type_powder;
#SerializedName("released")
boolean isReleased;
String apa_number;
String poster;
String location;
String date_expired;
private Movie() {
}
public String getApa_number() {
return this.apa_number;
}
public String getDirector() {
return this.director;
}
public String getPoster() {
return this.poster;
}
public String getYear_created() {
return this.year_created;
}
public String getType_powder() {
return this.type_powder;
}
public String getLocation() {
return this.location;
}
public String getDate_expired() {
return this.date_expired;
}
public boolean isReleased() {
return this.isReleased;
}
}
NotificationCompat.Builder notification;
private static final int uniqueID = 250298;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ticket_result);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
this.txtApa_number = (TextView) findViewById(R.id.apa_number);
this.txtDirector = (TextView) findViewById(R.id.director);
this.txtYear_created = (TextView) findViewById(R.id.year_created);
this.txtLocation = (TextView) findViewById(R.id.location);
this.txtDate_expired = (TextView) findViewById(R.id.date_expired);
this.imgPoster = (ImageView) findViewById(R.id.poster);
this.txtType_powder = (TextView) findViewById(R.id.type_powder);
this.btnBuy = (Button) findViewById(R.id.btn_buy);
this.imgPoster = (ImageView) findViewById(R.id.poster);
this.txtError = (TextView) findViewById(R.id.txt_error);
this.ticketView = (TicketView) findViewById(R.id.layout_ticket);
this.progressBar = (ProgressBar) findViewById(R.id.progressBar);
String barcode = getIntent().getStringExtra("code");
if (TextUtils.isEmpty(barcode)) {
Toast.makeText(getApplicationContext(), "Barcode is empty!", Toast.LENGTH_LONG).show();
finish();
}
searchBarcode(barcode);
}
public void btn_buyClicked(View view) {
notification.setSmallIcon(R.drawable.qrcode);
notification.setTicker("This is the ticker");
notification.setWhen(System.currentTimeMillis());
notification.setContentTitle("Fire Extinguisher Scanner");
Intent intent = new Intent(this, Test.class);
startActivity(new Intent(TicketResultActivity.this, Test.class));
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setContentIntent(pendingIntent);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify(uniqueID, notification.build());
}
private void searchBarcode(String barcode) {
MyApplication.getInstance().addToRequestQueue(new JsonObjectRequest(Request.Method.GET, barcode, null, new Listener<JSONObject>() {
public void onResponse(JSONObject response) {
Log.e(TicketResultActivity.TAG, "Ticket response: " + response.toString());
if (response.has("error")) {
TicketResultActivity.this.showNoTicket();
} else {
TicketResultActivity.this.renderMovie(response);
}
}
}, new ErrorListener() {
public void onErrorResponse(VolleyError error) {
Log.e(TicketResultActivity.TAG, "Error: " + error.getMessage());
TicketResultActivity.this.showNoTicket();
}
}));
}
private void showNoTicket() {
this.txtError.setVisibility(View.VISIBLE);
this.ticketView.setVisibility(View.GONE);
this.progressBar.setVisibility(View.GONE);
}
public void renderMovie(JSONObject response) {
try {
Movie movie = (Movie) new Gson().fromJson(response.toString(), Movie.class);
if (movie != null) {
this.txtApa_number.setText(movie.getApa_number());
this.txtDirector.setText(movie.getDirector());
this.txtYear_created.setText(movie.getYear_created());
this.txtType_powder.setText(movie.getType_powder());
this.txtDate_expired.setText(BuildConfig.FLAVOR + movie.getDate_expired());
this.txtLocation.setText(movie.getLocation());
Glide.with(this).load(movie.getPoster()).into(this.imgPoster);
notification.setContentText("Fire Extinguisher "+ movie.getApa_number()+"successfully remind!");
if (movie.isReleased()) {
this.btnBuy.setText(getString(R.string.btn_buy_now));
this.btnBuy.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
} else {
this.btnBuy.setText(getString(R.string.btn_buy_now));
this.btnBuy.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
}
this.ticketView.setVisibility(View.VISIBLE);
this.progressBar.setVisibility(View.GONE);
return;
}
showNoTicket();
} catch (JsonSyntaxException e) {
Log.e(TAG, "JSON Exception: " + e.getMessage());
showNoTicket();
Toast.makeText(getApplicationContext(), "Error occurred. Check your LogCat for full report", Toast.LENGTH_SHORT).show();
} catch (Exception e2) {
showNoTicket();
Toast.makeText(getApplicationContext(), "Error occurred. Check your LogCat for full report", Toast.LENGTH_SHORT).show();
}
}
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
}
This is my TicketResultActivity.java class UPDATED CODE
private static class Movie implements Parcelable {
String director;
String year_created;
String type_powder;
#SerializedName("released")
boolean isReleased;
String apa_number;
String poster;
String location;
String date_expired;
public Movie() {
}
public Movie(Parcel in) {
director = in.readString();
year_created = in.readString();
type_powder = in.readString();
isReleased = in.readByte() != 0;
apa_number = in.readString();
poster = in.readString();
location = in.readString();
date_expired = in.readString();
}
public String getApa_number(){
return this.apa_number;
}
public String getYear_created() {
return year_created;
}
public String getType_powder() {
return type_powder;
}
public String getDirector() {
return director;
}
public String getPoster() {
return poster;
}
public String getLocation() {
return location;
}
public boolean isReleased() {
return isReleased;
}
public String getDate_expired() {
return date_expired;
}
public void setApa_number(String apa_number){
this.apa_number = apa_number;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(director);
dest.writeString(year_created);
dest.writeString(type_powder);
dest.writeByte((byte) (isReleased ? 1 : 0));
dest.writeString(apa_number);
dest.writeString(poster);
dest.writeString(location);
dest.writeString(date_expired);
}
public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>() {
#Override
public Movie createFromParcel(Parcel in) {
return new Movie(in);
}
#Override
public Movie[] newArray(int size) {
return new Movie[size];
}
};
#Override
public int describeContents() {
return 0;
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ticket_result);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
this.txtApa_number = (TextView) findViewById(R.id.apa_number);
this.txtDirector = (TextView) findViewById(R.id.director);
this.txtYear_created = (TextView) findViewById(R.id.year_created);
this.txtLocation = (TextView) findViewById(R.id.location);
this.txtDate_expired = (TextView) findViewById(R.id.date_expired);
this.imgPoster = (ImageView) findViewById(R.id.poster);
this.txtType_powder = (TextView) findViewById(R.id.type_powder);
this.btnBuy = (Button) findViewById(R.id.btn_buy);
this.imgPoster = (ImageView) findViewById(R.id.poster);
this.txtError = (TextView) findViewById(R.id.txt_error);
this.ticketView = (TicketView) findViewById(R.id.layout_ticket);
this.progressBar = (ProgressBar) findViewById(R.id.progressBar);
String barcode = getIntent().getStringExtra("code");
if (TextUtils.isEmpty(barcode)) {
Toast.makeText(getApplicationContext(), "Barcode is empty!", Toast.LENGTH_LONG).show();
finish();
}
searchBarcode(barcode);
}
public void btn_buyClicked(View view) {
// In activity or fragment
Movie movie = new Movie();
movie.setApa_number("xyz");
Intent intent = new Intent(this, Test.class);
intent.putExtra("parcel_data", movie);
startActivity(intent);
}
private void searchBarcode(String barcode) {
MyApplication.getInstance().addToRequestQueue(new JsonObjectRequest(Request.Method.GET, barcode, null, new Listener<JSONObject>() {
public void onResponse(JSONObject response) {
Log.e(TicketResultActivity.TAG, "Ticket response: " + response.toString());
if (response.has("error")) {
TicketResultActivity.this.showNoTicket();
} else {
TicketResultActivity.this.renderMovie(response);
}
}
}, new ErrorListener() {
public void onErrorResponse(VolleyError error) {
Log.e(TicketResultActivity.TAG, "Error: " + error.getMessage());
TicketResultActivity.this.showNoTicket();
}
}));
}
private void showNoTicket() {
this.txtError.setVisibility(View.VISIBLE);
this.ticketView.setVisibility(View.GONE);
this.progressBar.setVisibility(View.GONE);
}
public void renderMovie(JSONObject response) {
try {
Movie movie = (Movie) new Gson().fromJson(response.toString(), Movie.class);
if (movie != null) {
this.txtApa_number.setText(movie.getApa_number());
this.txtDirector.setText(movie.getDirector());
this.txtYear_created.setText(movie.getYear_created());
this.txtType_powder.setText(movie.getType_powder());
this.txtDate_expired.setText(BuildConfig.FLAVOR + movie.getDate_expired());
this.txtLocation.setText(movie.getLocation());
Glide.with(this).load(movie.getPoster()).into(this.imgPoster);
if (movie.isReleased()) {
this.btnBuy.setText(getString(R.string.btn_buy_now));
this.btnBuy.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
} else {
this.btnBuy.setText(getString(R.string.btn_buy_now));
this.btnBuy.setTextColor(ContextCompat.getColor(this, R.color.colorPrimary));
}
this.ticketView.setVisibility(View.VISIBLE);
this.progressBar.setVisibility(View.GONE);
return;
}
showNoTicket();
} catch (JsonSyntaxException e) {
Log.e(TAG, "JSON Exception: " + e.getMessage());
showNoTicket();
Toast.makeText(getApplicationContext(), "Error occurred. Check your LogCat for full report", Toast.LENGTH_SHORT).show();
} catch (Exception e2) {
showNoTicket();
Toast.makeText(getApplicationContext(), "Error occurred. Check your LogCat for full report", Toast.LENGTH_SHORT).show();
}
}
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
}
This is Test.java Class
public class Test extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Movie movie = (Movie) getIntent().getParcelableExtra("parcel_data");
String apa_number = movie.getApa_number();
TextView textView1 = findViewById(R.id.textView2);
textView1.setText(apa_number);
}
}
Use Parcelable is an interface. A class who implements Parcelable can write to and read from a Parcel.
You need to follow 3 points to create a Parcelable class.
A Class must implement Parcelable interface
A Class must have a non-null static field CREATOR of a type that implements Parcelable.Creator interface.
Override writeToParcel method and write member variable in Parcel. Make sure to read variables in the same sequence in which they are written in Parcel. Order of read and write matters.
private class Movie implements Parcelable{
String director;
String year_created;
String type_powder;
#SerializedName("released")
boolean isReleased;
String apa_number;
String poster;
String location;
String date_expired;
public Movie() {
}
// In constructor you will read the variables from Parcel. Make sure to read them in the same sequence in which you have written them in Parcel.
public Movie(Parcel in) {
director = in.readString();
year_created = in.readString();
release_date = in.readString();
poster = in.readString();
}
public String getApa_number() {
return this.apa_number;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
// This is where you will write your member variables in Parcel. Here you can write in any order. It is not necessary to write all members in Parcel.
#Override
public void writeToParcel(Parcel dest, int flags) {
// Write data in any order
dest.writeString(director);
dest.writeString(year_created);
dest.writeString(release_date);
dest.writeString(poster);
}
// This is to de-serialize the object
public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>(){
public Movie createFromParcel(Parcel in) {
return new Movie(in);
}
public Movie[] newArray(int size) {
return new Movie[size];
}
};
}
Now you can pass a Parcelable object using Intent.
// In activity or fragment
Movie movie = new Movie();
movie.setDirector("xyz");
// now you can set all values like :year created, is released whatever.
// using context and next component class to create intent
Intent intent = new Intent(this, NextActivity.class);
// using putExtra(String key, Parcelable value) method
intent.putExtra(“parcel_data”, movie);
startActivity(intent);
You can access this data in NextActivity –
public class NextActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// Using getParcelableExtra(String key) method
Movie movie = (Movie) getIntent().getParcelableExtra("parcel_data");
String director = movie.getDirector();
}
}
There are so many ways to send data from one activity to another activity. If you Have Primitive or Json string type data then you can directly put that data into the intent.
But if in case you have Model class and you need to pass it. Then you have two ways:
Serializable
Parcelable
But Android recommend to use Parcelable.
You can also add plugin to android studio to generate the parcelable code.

JSON Property not binding to JSON.NET PropertyName in ASP.NET MVC 5 Post request

I am banging my head hear on why my property ReCaptchaResponse JSONProperty will not bind to my model. The others do just find, and my JSON Value Provider class hits just fine. Any clue at all? It is always NULL.
Ajax Request
{"Name":"Joe","Email":"","Message":"","g-recaptcha-response":"data"}
ContactUsController.cs
[HttpPost]
public virtual ActionResult Index(ContactUsModel model)
{
_contactUsService.ContactUs(model);
return Json(new SuccessResponse("Submitted Successfully"));
}
ContactUsMode.cs
[JsonObject, DataContract]
public class ContactUsModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Message { get; set; }
[JsonProperty(PropertyName = "g-recaptcha-response"), DataMember(Name = "g-recaptcha-response")]
public string ReCaptchaResponse { get; set; }
}
JsonNetValueProviderFactory.cs
namespace Tournaments.Models.Mvc
{
public class JsonNetValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
// first make sure we have a valid context
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
// now make sure we are dealing with a json request
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
return null;
// get a generic stream reader (get reader for the http stream)
var streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
// convert stream reader to a JSON Text Reader
var jsonReader = new JsonTextReader(streamReader);
// tell JSON to read
if (!jsonReader.Read())
return null;
// make a new Json serializer
var jsonSerializer = new JsonSerializer();
jsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
// add the dyamic object converter to our serializer
jsonSerializer.Converters.Add(new ExpandoObjectConverter());
// use JSON.NET to deserialize object to a dynamic (expando) object
Object jsonObject;
// if we start with a "[", treat this as an array
if (jsonReader.TokenType == JsonToken.StartArray)
jsonObject = jsonSerializer.Deserialize<List<ExpandoObject>>(jsonReader);
else
jsonObject = jsonSerializer.Deserialize<ExpandoObject>(jsonReader);
// create a backing store to hold all properties for this deserialization
var backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
// add all properties to this backing store
AddToBackingStore(backingStore, String.Empty, jsonObject);
// return the object in a dictionary value provider so the MVC understands it
return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
}
private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
{
var d = value as IDictionary<string, object>;
if (d != null)
{
foreach (KeyValuePair<string, object> entry in d)
{
AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
}
return;
}
var l = value as IList;
if (l != null)
{
for (int i = 0; i < l.Count; i++)
{
AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
}
return;
}
// primitive
backingStore[prefix] = value;
}
private static string MakeArrayKey(string prefix, int index)
{
return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
}
private static string MakePropertyKey(string prefix, string propertyName)
{
return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
}
}
}
Try ModelBinder. ValueProviderFactory does not work because of ExpandoObject.
internal class JsonNetModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
controllerContext.HttpContext.Request.InputStream.Position = 0;
var stream = controllerContext.RequestContext.HttpContext.Request.InputStream;
var readStream = new StreamReader(stream, Encoding.UTF8);
var json = readStream.ReadToEnd();
return JsonConvert.DeserializeObject(json, bindingContext.ModelType);
}
}
ContactUsController.cs
[HttpPost]
public virtual ActionResult Index([ModelBinder(typeof(JsonNetModelBinder))]ContactUsModel model)
{
_contactUsService.ContactUs(model);
return Json(new SuccessResponse("Submitted Successfully"));
}

Spring Framework Default Error Page to JSON

Sorry,
if i am asking for lazy solution.
#SpringBootConfiguration
public class RestWebApplication {
public static void main(String[] args) {
SpringApplication.run(RestWebApplication.class, args);
}
}
But when nothing is implemented, I expected
$ curl localhost:8080
{"timestamp":1384788106983,"error":"Not Found","status":404,"message":""}
But Got
<!DOCTYPE html><html><head><title>Apache Tomcat/8.5.9 - Error report</title><style type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style> </head><body><h1>HTTP Status 404 - /</h1><div class="line"></div><p><b>type</b> Status report</p><p><b>message</b> <u>/</u></p><p><b>description</b> <u>The requested resource is not available.</u></p><hr class="line"><h3>Apache Tomcat/8.5.9</h3></body></html>
Did i miss something ?
So that i the error page is redirected as JSON Output?
Thanks in credit for your help.
You can try to use #ControllerAdvice that help for custom exception handling in spring.
This is the code I use :
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler()
public ResponseEntity<Exception> defaultErrorHandler(Exception e) throws Exception {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
#ExceptionHandler()
public ResponseEntity<ShemoException> defaultErrorHandler(ShemoException e) throws Exception {
return new ResponseEntity<>(e,HttpStatus.NOT_FOUND);
}
This is custom Exception class:
import com.google.gson.JsonSyntaxException;
public class ShemoResponseMessage {
private int returnCode;
private String returnStatus;
private String errorSource;
// constructor
public ShemoResponseMessage() {
returnCode = -1;
returnStatus = null;
errorSource = null;
}
// Constructor with individual response parts
public ShemoResponseMessage(int code, String status, String source) {
returnCode = code;
returnStatus = status;
errorSource = source;
}
public ShemoResponseMessage(String shemoResponse) {
this();
if (shemoResponse == null) {
return;
}
ShemoResponseMessage obj = null;
try {
obj = (ShemoResponseMessage) GsonUtils.createGson().fromJson(shemoResponse,
ShemoResponseMessage.class);
} catch (JsonSyntaxException e) {
returnCode = -1;
returnStatus = "";
errorSource = "";
return;
}
returnCode = obj.returnCode;
returnStatus = obj.returnStatus;
errorSource = obj.errorSource;
}
public ShemoResponseMessage(ShemoException e) {
this(e.getMessage());
}
// Copy constructor
public ShemoResponseMessage(ShemoResponseMessage obj) {
this(obj.getReturnCode(), obj.getReturnStatus(), obj.getErrorSource());
}
// getters
public int getReturnCode() {
return returnCode;
}
public String getReturnStatus() {
return returnStatus;
}
public String getErrorSource() {
return errorSource;
}
// Get the json error message back. Creates a formatted message which can be used for throwing API exceptions
public String getShemoExeption() {
String jsonResponse = GsonUtils.createGson().toJson(this, ShemoResponseMessage.class);
return jsonResponse;
}
}
You can return any message you like
UPDATED
This is my custom exception class you can modify it per your need:
public class ShemoException extends Exception {
private static final long serialVersionUID = 1L;
Integer errorCode;
String errorMessage;
public ShemoException(Exception e) {
super(e);
errorCode = -1;
errorMessage = "";
String classNameMessage = getExceptionClassName(e);
if (e.getMessage() != null)
errorMessage = classNameMessage + ", " + e.getMessage();
else
errorMessage = classNameMessage;
}
private String getExceptionClassName(Exception e) {
String className = new String();
String classNameMessage = new String("");
Class<? extends Exception> eClass = e.getClass();
if (eClass != null) {
className = eClass.getSimpleName();
String words[] = className.split("(?=[A-Z])"); // Split Name by Upper Case for readability
// put the Name back together, now with spaces between words
for (int i = 0; i < words.length; i++) {
String word = words[i];
if (i > 0 && word.length() > 1)
classNameMessage = classNameMessage.concat(" ");
classNameMessage = classNameMessage.concat(word);
}
}
return classNameMessage.trim();
}
public ShemoException(Integer errorCode, String errorMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public ShemoException(Integer errorCode, ShemoResponseMessage responseMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = responseMessage.getShemoExeption();
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
#Override
public String getMessage() {
return getErrorMessage();
}
}
GsonUtils class:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Created by Shemo on 11/24/2015.
*/
public class GsonUtils {
public static String defaultDateTimeFormat = "yyyy-MM-dd'T'HH:mm:ssZ";
private static GsonBuilder gsonBuilder = new GsonBuilder().setDateFormat(defaultDateTimeFormat);
/***
* Creates a GSON instance from the builder with the default date/time format
*
* #return the GSON instance
*/
public static Gson createGson() {
// Create with default params
gsonBuilder = gsonBuilder.setDateFormat(defaultDateTimeFormat);
return gsonBuilder.create();
}
/***
* Creates a GSON instance from the builder specifying custom date/time format
*
* #return the GSON instance
*/
public static Gson createGson(String dateTimeFormat) {
// Create with the specified dateTimeFormat
gsonBuilder = gsonBuilder.setDateFormat(dateTimeFormat);
return gsonBuilder.create();
}
}
GSON library:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>

Is there a way to ignore JsonSyntaxException in Gson

I have a json that looks like this:
[
{
_id: "54b8f62fa08c286b08449b8f",
loc: [
36.860983,
31.0567
]
},
{
_id: "54b8f6aea08c286b08449b93",
loc: {
coordinates: [ ]
}
}
]
As you can see, loc object is sometimes is a json object, sometimes is a double array. Without writing a custom deserializer, is there a way to avoid JsonSyntaxException and set the loc object to null when it is a json object rather than a double array.
There aren't any easy way (I mean a property/method call at Gson) for custom seralization/deserialization of a specific field at a json value.
You can see source code of com.google.gson.internal.bind.ReflectiveTypeAdapterFactory, and debug on its inner class Adapter's read method. (That's where your JsonSyntaxException occurs)
You can read Custom serialization for JUST specific fields and track its links. It may be implemented at future release of Gson. (Not available at latest release 2.2.4)
I would write some code for this. Maybe that's not what you are looking for but it may help somebody else.)
Solution 1 (This has less code compared with the second solution but second solution's performance is much more better):
public class SubClass extends BaseClass {
private double[] loc;
}
public class BaseClass {
#SerializedName("_id")
private String id;
}
public class CustomTypeAdapter extends TypeAdapter<BaseClass> {
private Gson gson;
public CustomTypeAdapter() {
this.gson = new Gson();
}
#Override
public void write(JsonWriter out, BaseClass value)
throws IOException {
throw new RuntimeException("Not implemented for this question!");
}
#Override
public BaseClass read(JsonReader in) throws IOException {
BaseClass instance;
try {
instance = gson.fromJson(in, SubClass.class);
} catch (Exception e) {
e.printStackTrace();
instance = gson.fromJson(in, BaseClass.class);
}
return instance;
}
}
Test:
private void test() {
String json = "[{_id:\"54b8f62fa08c286b08449b8f\",loc:[36.860983,31.0567]},{_id:\"54b8f6aea08c286b08449b93\",loc:{coordinates:[]}}]";
Type collectionType = new TypeToken<List<BaseClass>>(){}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(BaseClass.class, new CustomTypeAdapter()).create();
List<BaseClass> list = gson.fromJson(json, collectionType);
for(BaseClass item : list) {
if(item instanceof SubClass) {
System.out.println("item has loc value");
SubClass subClassInstance = (SubClass)item;
} else {
System.out.println("item has no loc value");
BaseClass baseClassInstance = item;
}
}
}
Solution 2 (It is one of the Gson Developers suggestion. See original post.):
Copy below class to your project. It is going to be a base class for your custom TypeAdapterFactorys.
public abstract class CustomizedTypeAdapterFactory<C>
implements TypeAdapterFactory {
private final Class<C> customizedClass;
public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
this.customizedClass = customizedClass;
}
#SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return type.getRawType() == customizedClass
? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
: null;
}
private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<C>() {
#Override public void write(JsonWriter out, C value) throws IOException {
JsonElement tree = delegate.toJsonTree(value);
beforeWrite(value, tree);
elementAdapter.write(out, tree);
}
#Override public C read(JsonReader in) throws IOException {
JsonElement tree = elementAdapter.read(in);
afterRead(tree);
return delegate.fromJsonTree(tree);
}
};
}
/**
* Override this to muck with {#code toSerialize} before it is written to
* the outgoing JSON stream.
*/
protected void beforeWrite(C source, JsonElement toSerialize) {
}
/**
* Override this to muck with {#code deserialized} before it parsed into
* the application type.
*/
protected void afterRead(JsonElement deserialized) {
}
}
Write your POJO and your custom CustomizedTypeAdapterFactory. Override afterRead method and handle double array as you asked at your question:
public class MyClass {
#SerializedName("_id")
private String id;
private double[] loc;
// getters/setters
}
private class MyClassTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyClass> {
private MyClassTypeAdapterFactory() {
super(MyClass.class);
}
#Override protected void afterRead(JsonElement deserialized) {
try {
JsonArray jsonArray = deserialized.getAsJsonObject().get("loc").getAsJsonArray();
System.out.println("loc is not a double array, its ignored!");
} catch (Exception e) {
deserialized.getAsJsonObject().remove("loc");
}
}
}
Test:
private void test() {
String json = "[{_id:\"54b8f62fa08c286b08449b8f\",loc:[36.860983,31.0567]},{_id:\"54b8f6aea08c286b08449b93\",loc:{coordinates:[]}}]";
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
.create();
Type collectionType = new TypeToken<List<MyClass>>(){}.getType();
List<MyClass> list = gson.fromJson(json, collectionType);
for(MyClass item : list) {
if(item.getLoc() != null) {
System.out.println("item has loc value");
} else {
System.out.println("item has no loc value");
}
}
}
This is how I did this. It is shorter, but I think #DevrimTuncers answer is the best one.
//This is just Double array to use as location object
public class Location extends ArrayList<Double> {
public Double getLatidute() {
if (this.size() > 0) {
return this.get(0);
} else {
return (double) 0;
}
}
public Double getLongitude() {
if (this.size() > 1) {
return this.get(1);
} else {
return (double) 0;
}
}
public static class LocationDeserializer implements JsonDeserializer<Location> {
#Override
public Location deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
JsonArray array = json.getAsJsonArray();
Location location = new Location();
for (int i = 0; i < array.size(); i++) {
location.add(array.get(i).getAsDouble());
}
return location;
} catch (Exception e) {
return null;
}
}
}
}

Struts2 Action being called twice if result type is json

I have an Action class with 4 action methods.
All four action action methods use a json result.
Via logging statements and debugging, I have verified that if I call action method 1, action method 2 and 3 are also called. But not 4. Finally, action method 1 is called again and the json result is generated
If I change the result type of Action method 1 to the default dispatcher with a jsp location, only action method 1 is called. (this is the behavior I want with the json result)
Hope that makes sense.
Anyone have any ideas?
This question was asked here https://stackoverflow.com/questions/3767698/struts2-if-result-type-json-and-method-defined-then-all-methods-get-invoked
But there was no answer.
Please let me know if you need more information.
#ResultPath("/WEB-INF/jsp/dta/")
public class GroupEntityAction extends BaseAction {
/**
*
*/
private static final long serialVersionUID = 6750675222824235086L;
private static Logger log = Logger.getLogger(GroupEntityAction.class);
private List<EntityBusiness> theUnusedEntityBusinessList;
private String assignedEntities[];
private long groupId;
private long businessId;
private String parentMe;
private long rptYear;
private String ssoId;
private String isSubmitted;
private String delimGoLiveEmails;
private List<String> theEmailList;
#Action(value = "ajaxGetAvailableEntityList",
results = { #Result(name = "success", type = "json") }
,
interceptorRefs = { #InterceptorRef("dtaStack"),
#InterceptorRef(value = "dtaStack", params = { "appInterceptor.allowedRoles", "ADMIN" }) }
)
public String getEntityListsByBusiness() throws Exception {
if (rptYear == 0) {
return SUCCESS;
}
LookupService theSvc = new LookupService();
if (businessId != 0) {
setTheUnusedEntityBusinessList(theSvc.getAvailableEntityListBizExceptIds(rptYear, businessId, ssoId, assignedEntities));
} else {
setTheUnusedEntityBusinessList(theSvc.getAvailableEntityListParentMeExceptIds(rptYear, parentMe, ssoId, assignedEntities));
}
log.debug(theUnusedEntityBusinessList.size());
return SUCCESS;
}
#Action(value = "ajaxToggleGroupBusinessSubmitted",
results = { #Result(name = "success", type = "json") }
,
interceptorRefs = { #InterceptorRef("dtaStack") }
)
public String toggleGroupBusinessReview() {
try {
new ProformaService().toggleIsSubmitted(getCurrentUser().getSsoId(), groupId, rptYear, businessId);
} catch (SQLException e) {
log.error(e.getMessage());
return ERROR;
}
return SUCCESS;
}
#Action(value = "ajaxGetGoLiveEmailList",
results = { #Result(type = "json") }
,
interceptorRefs = { #InterceptorRef("dtaStack"),
#InterceptorRef(value = "dtaStack", params = { "appInterceptor.allowedRoles", "ADMIN" }) }
)
public String getGoLiveEmailList() {
try {
List<TaxUser> theUserList = new SecurityService().getAll();
List<String> theEmailList = new ArrayList<String>();
for (TaxUser theUser : theUserList) {
if ((!theUser.getRoles().contains("ADMIN")) && (theUser.getIsActive().equalsIgnoreCase("Y"))) {
if (!theEmailList.contains(theUser.getEmail())) {
theEmailList.add(theUser.getEmail());
}
}
}
setDelimGoLiveEmails(StringUtils.join(theEmailList.toArray(), "|"));
setTheEmailList(theEmailList);
} catch (SQLException e) {
log.error(e.getMessage());
return ERROR;
}
return SUCCESS;
}
#Action(value = "ajaxGetChaserEmailList",
results = { #Result(name = "success", type = "json") }
,
interceptorRefs = { #InterceptorRef("dtaStack"),
#InterceptorRef(value = "dtaStack", params = { "appInterceptor.allowedRoles", "ADMIN" }) }
)
public String getChaserEmailList() {
try {
List<String> theEmailList = new LookupService().getChaserEmailList();
setDelimGoLiveEmails(StringUtils.join(theEmailList.toArray(), "|"));
setTheEmailList(theEmailList);
} catch (SQLException e) {
log.error(e.getMessage());
return ERROR;
}
return SUCCESS;
}
public void setTheUnusedEntityBusinessList(
List<EntityBusiness> theUnusedEntityBusinessList) {
this.theUnusedEntityBusinessList = theUnusedEntityBusinessList;
}
public List<EntityBusiness> getTheUnusedEntityBusinessList() {
return theUnusedEntityBusinessList;
}
public void setAssignedEntities(String assignedEntities[]) {
this.assignedEntities = assignedEntities;
}
public String[] getAssignedEntities() {
return assignedEntities;
}
public void setGroupId(long groupId) {
this.groupId = groupId;
}
public long getGroupId() {
return groupId;
}
public void setBusinessId(long businessId) {
this.businessId = businessId;
}
public long getBusinessId() {
return businessId;
}
public void setParentMe(String parentMe) {
this.parentMe = parentMe;
}
public String getParentMe() {
return parentMe;
}
public void setRptYear(long rptYear) {
this.rptYear = rptYear;
}
public long getRptYear() {
return rptYear;
}
public void setSsoId(String ssoId) {
this.ssoId = ssoId;
}
public String getSsoId() {
return ssoId;
}
public void setIsSubmitted(String isSubmitted) {
this.isSubmitted = isSubmitted;
}
public String getIsSubmitted() {
return isSubmitted;
}
public void setDelimGoLiveEmails(String delimGoLiveEmails) {
this.delimGoLiveEmails = delimGoLiveEmails;
}
public String getDelimGoLiveEmails() {
return delimGoLiveEmails;
}
public void setTheEmailList(List<String> theEmailList) {
this.theEmailList = theEmailList;
}
public List<String> getTheEmailList() {
return theEmailList;
}
}
In this action class, I attempting to call ajaxGetGoLiveEmailList, and what I get is ajaxGetGoLiveEmailList called first, and then ajaxGetChaserEmailList, and then ajaxGetAvailableEntityList, and then ajaxGetGoLiveEmailList gets called again. ajaxToggleGroupBusinessSubmitted is skipped.
If I change the result annotation of ajaxGetGoLiveEmailList to
results={#Result(location="something.jsp")
, only ajaxGetGoLiveEmailList get called.
When I look at the config browser, all the action mapping are configured correctly, pointing to the correct method calls.
JSON plugin may be calling all your methods that start with "get" in an attempt to serialize them for output. Try renaming your methods to something else.