I'm pulling my hair out on this one. I'm trying to implement a ADO.Net Data Service that uses a Linq to SQL data context. I thought I had it working, but the URL for one of my tables always gets an exception.
The obvious difference between the table that isn't working and the ones that are, is that the one getting the exception is using a Guid, which is the primary key. The Guid is a UserID, which actually relates to the UserId used by ASP.net Membership. (I'm not exposing the ASP.net Membership tables, but I'm guessing these would break too if I were.)
It is a very simple table:
Name: UserDetails :: | Guid UserID | int GroupID (foreign key) | string Name |
Anybody know if there's a trick to getting Guids to work? Or if maybe this is an entirely different problem?
Here's the exception from the service:
An error occurred while processing this request.
InnerError: An error occurred while processing this request.
Type: System.InvalidOperationException
StackTrace:
t System.Data.Services.Serializers.SyndicationSerializer.WriteComplexObjectValue(Object element, String propertyName, ResourceType expectedType, String relativeUri, DictionaryContent content)
at System.Data.Services.Serializers.SyndicationSerializer.WriteObjectProperties(IExpandedResult expanded, Object customObject, ResourceType resourceType, Uri absoluteUri, String relativeUri, SyndicationItem item, DictionaryContent content)
at System.Data.Services.Serializers.SyndicationSerializer.WriteComplexObjectValue(Object element, String propertyName, ResourceType expectedType, String relativeUri, DictionaryContent content)
at System.Data.Services.Serializers.SyndicationSerializer.WriteObjectProperties(IExpandedResult expanded, Object customObject, ResourceType resourceType, Uri absoluteUri, String relativeUri, SyndicationItem item, DictionaryContent content)
at System.Data.Services.Serializers.SyndicationSerializer.WriteComplexObjectValue(Object element, String propertyName, ResourceType expectedType, String relativeUri, DictionaryContent content)
at System.Data.Services.Serializers.SyndicationSerializer.WriteObjectProperties(IExpandedResult expanded, Object customObject, ResourceType resourceType, Uri absoluteUri, String relativeUri, SyndicationItem item, DictionaryContent content)
at System.Data.Services.Serializers.SyndicationSerializer.WriteEntryElement(IExpandedResult expanded, Object element, Type expectedType, Uri absoluteUri, String relativeUri, SyndicationItem target)
at System.Data.Services.Serializers.SyndicationSerializer.<DeferredFeedItems>d__0.MoveNext()
at System.ServiceModel.Syndication.Atom10FeedFormatter.WriteItems(XmlWriter writer, IEnumerable`1 items, Uri feedBaseUri)
at System.ServiceModel.Syndication.Atom10FeedFormatter.WriteFeedTo(XmlWriter writer, SyndicationFeed feed, Boolean isSourceFeed)
at System.ServiceModel.Syndication.Atom10FeedFormatter.WriteFeed(XmlWriter writer)
at System.ServiceModel.Syndication.Atom10FeedFormatter.WriteTo(XmlWriter writer)
at System.Data.Services.Serializers.SyndicationSerializer.WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, Boolean hasMoved)
at System.Data.Services.Serializers.Serializer.WriteRequest(IEnumerator queryResults, Boolean hasMoved)
at System.Data.Services.ResponseBodyWriter.Write(Stream stream)
That looks like a bug. I suggest you report it at http://connect.microsoft.com/visualstudio/, then post the URL of your bug report here, so we can vote on it.
I found the fix to my problem. It actually wasn't related to using a Guid value at all.
I had to add an [IgnoreProperties("User")] to my UserDetail class in Linq to SQL.
The "User" Property is a relation to the "User" class that holds the ASP.Net Memberships user informations (actual table name is aspnet_Users). I have the Linq to SQL "User" class ignored by the data service so I think this must have been the problem.
What threw me way off was that the data service wasn't throwing any errors when I accessed it. With all the other properties, where I either had to add a DataServiceKey() or IgnoreProperties() decoration, when I accessed DataService.svc, I would get an exception complaining about the problem property. For whatever reason, it didn't give me a problem with this one property though, so I wasn't aware anything was wrong. And as you can see above, when I did get an exception, it wasn't useful.
So to any others using Linq To SQL with ADO.Net Data Services, this is the lesson:
Make sure you use IgnoreProperties() on any property that references a class you are ignoring.
Related
When the entity classes were first created they were modeled in JPA to match database field names. For our rest endpoints we used JsonProperty to let Jackson auto map names the client would expect they are kabob case. However we are now implementing search functionality and are building it using QueryDSL. It works and works well as long as we use the internal field names.
What we are looking to do though is accept querystrings with the JsonProperty Name.
Like: campaign-grouping-code=123 vs groupingCode=123
I am assuming it would probably have to be done in the Q Classes that are generated but can't find much documentation on them.
#Column(name = "GROUPING_CODE")
private String groupingCode;
#JsonProperty("campaign-grouping-code")
#NotBlank(message = "1", groups = CampaignFieldCheck.class)
#Size(max = 40, message = "100", groups = CampaignFieldCheck.class)
public String getGroupingCode() {
return groupingCode;
}
public void setGroupingCode(String groupingCode) {
this.groupingCode = groupingCode;
}
Quite simply: no that is not possible because the Spring Data endpoints use the values from the attributes in the metamodel, which in the case for querydsl-jpa are always the JPA attribute names, which are in turn derived from the field names, and - is not a valid character in a Java field name.
So there isnt a way to get the jsonproperty names to work. There is a workaround aliasing.. In this link you can find the black list and white list settings. Aliases are automatically white listed. but the aliases can be mapped via whatever text string makes sense to the field names.
https://gt-tech.bitbucket.io/spring-data-querydsl-value-operators/README.html
I would like to be able to write to a log the properties of various objects that I might pass to my LogEvent function. I have various objects defined, such as Town, Taxpayer, TaxedProperty, TaxAccount, etc. in my ASP.NET MVC application. I would like to write to some log passing in my object along with a bunch of other information.
I am writing this to a database table and I have a large text field in which I can insert a representation of the object that was being processed when a bad thing happened. Perhaps the user was trying to edit a taxpayer they did not have access to. So, I would write to the log that this user tried to access this taxpayer and I would do this:
kurantLogic.LogEvent(userName, null, null, Request.HttpMethod, "Taxpayers/Edit: Access denied because user does not have access to the taxpayer with ID=" + taxpayer.ID, false, taxpayer);
the last argument is the taxpayer object, but it could be some other object, such as a town. Here is another example:
kurantLogic.LogEvent(userName, null, null, Request.HttpMethod, "Towns/SetUpYourTown: Access denied because user does not have access to the town with ID=" + town.ID, false, town);
or here is an example of logging a good thing:
kurantLogic.LogEvent(userName, null, null, Request.HttpMethod, "Towns/SetUpYourTown: Success, Town edit saved = " + town.Name, false, town);
I would like to list out the fields in the town or taxpayer or whatever object is passed in without having to hard code all the properties of the thing I might pass in .
Here is my code that I started with:
public void LogEvent(string userName, int? userID, int? townID, string httpVerb, string description, bool isError, Object obj)
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MaxDepth = 1;
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.NullValueHandling = NullValueHandling.Ignore;
string bigText = JsonConvert.SerializeObject(obj, settings);
LogEvent(userName, userID, townID, httpVerb, description, isError, bigText);
}
That kind of worked except that the JSON converter wrote out all the collections of things that the object contained, and I cannot find a way to suppress that. So, if there is a way to make JSON.SerializeObject include only the properties that are strings, ints, decimals, booleans, and not other objects, that would be a good solution. But I thought I could use Reflection to do it, but am having trouble with that.
My Taxpayer class has things like TaxedProperties that are associated with it, and I don't those logged. This is what I am getting now. At the end of this long JSON serialization you can see the simple properties listed.
Well, I could not post the JSON because StackOverflow thinks it looks like spam! I hope you can help.
I just want the "scalar" properties of the object logged without hard-coding. I would think that should be possible with either JSON or Reflection, but I am stumped.
You can tell Json.Net to ignore certain properties with the [JsonIgnore] attribute.
I'm quite new to JPA and using Java as backend for REST services, and I'd like to store a JSON into the database, and wanted to check what is the best way to do so. Please let me know in case I'm taking the "long path".
(I'm using Spring)
My data:
{
frequency: "Week"
isGoalOrMore: "true"
name: "Develop"
targetValue: "5"
type: "Average"
}
Habit.java
#Entity
#Table(name = "habits")
public class Habit {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_habit_type")
private HabitType type;
private boolean isGoalOrMore; //basically means, achieve goal or do more
private double targetValue;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_frequency")
private Frequency frequency;
//getters and setters
}
HabitType.java
#Entity
#Table(name = "habits_type")
public class HabitType {
#Id
private Long id;
private String description;
}
DB Model (mysql)
habits
--id
--name
--id_frequency
--id_habit_type
habits_type
--id
--description
Problem
When I try to save this data I receive an error as below:
com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class com.tiperapp.model.HabitType] from String value ('Average'); no single-String constructor/factory method
How can I solve it?
By reading some topics, one option would be to write a JSON deserializer. Other cases they solved it by fixing their JPA.
I'd like to know which would be recommend.. can you guys please help me how to deserialize this, the best way ?
The best solution, IMHO, is to think of the data that you send and receive to/from the browser (which I'll call the DTOs), and the data that you store in the database, as two distinct things.
Sure, they often look the same. But they're different. You can't have exactly the same model for both. For example:
the entities constitute a huge graph, often containing bidirectional associations, and which has no limit, since everything can be loaded lazily. For example, an order line has an order, which has a buyer, which has an address, which has a city, which has a country, which has... You can't reasonably serialize all this graph when the browser asks for an order line, otherwise you'll serialize half of the database to JSON.
the JSON sometimes has a different representation of the same thing than the database. For example, you store all the habit types in the database as a row with an ID and a description, but it seems the JSON only really cares about the description. The ID seems to be a way to avoid duplicating the same description everywhere.
many attributes of the entities can not be seen (for security reasons) by the end user, or are only relevant for some use cases, etc.
So I would thus use different classes for both. When you receive a HabitDTO as JSON, you deserialize it with Jackson to a HabitDTO instance, and then find the HabitType entity correspondint to the description in the DTO, and create/update the Habit entity instance based on the corresponding information in the HabitDTO.
To recap: the entities contain the complete business model of your application, used to implement all the functional use cases. The DTOs contain serialized information and are used to transfer a small part of the information to/from the client, often for a specific use case. Having a clear distinction between the two allows much more flexibility: you can change the underlying persistence model without changing the interface of your services, or vice versa.
Your json is wrong.
type is not mapped to a string but to an object. You can do that by using this :
{
frequency: "Week"
isGoalOrMore: "true"
name: "Develop"
targetValue: "5"
type: {
description: "A DESCRIPTION"
id: "Average"
}
}
I have developed this webapp using Spring MVC + Hibernate.
I retrieve all my objects in a Service then return them directly to the controller. These objects generally are lazily initialized so collections are empty.
So for object User:
User
{
int idUser;
City city;
String name;
List<User> friends;
}
I return an object with just idUser and name, City and Friends are not initialized.
I want to take advantage of all my services methods (without modifying them) to provide a REST api, so if from my ApiController I request to get user with id 1, I retrieve all useful information about this user in JSON.
I tried using GSON but as soon as it tries to jsonize the city object it crashes because it has been lazily initialized. Same goes for the friends collection.
For collections it's not much of a big deal since in my api I would have another request url where you can get all friends given a user Id, but in the case of relationships with a single object (like the city in this example), I would like to return the id of the City which by definition of Lazy loading is indeed set.
How can I tell GSON to jsonize just the cityId attribute of City instead of the whole object?
Will nulling the rest of collections be a good solution so they're not converted into JSON? Is there any other way to explicitly tell GSON to ignore these attributes?
I believe you need to put your gson.toJson(...) within a transaction, in springMVC typically #Transactional at controller method where you are doing the actual serialization will do.
If you really want to skip fields or selectively serialize fields of using Gson, you can check https://stackoverflow.com/a/3341439 for gson exclusion strategy. You can skip based on Field Annotation or field name or the entire referenced class.
Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return <class exclusion logic, return true for exclusion else false>;
}
public boolean shouldSkipField(FieldAttributes f) {
return <field exclusion logic>;
}
}).create();
For some reason, I haven't found any normal way to do the following:
I want to Post a json object, and add additional parameters to the call (in this case, an authentication token).
This is a simple RESTful server in myUrl/server, which should give access to different resources of a "person" in the url myUrl/server/person/personCode/resourceName.
GET is easy, and requires no object, only parameters.
The problem arrises when I get to POST - how do I attach the JSON, and keep the other parameters as well?
The class (much has been removed for clarity and proprietary reasons...):
//Handles the person's resources
#Path("/person/{personCode}/{resourceName}")
public class PersonResourceProvider {
#GET
#Produces("application/json")
public String getPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Authenticates the user in some way, throwing an exception when needed...
authenticate(personCode, auth_token, resourceName);
//Returns the resource somehow...
}
#POST
#Produces("application/json")
public String postPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("resourceData") String resourceData, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Again, authenticating
authenticate(personCode, auth_token, resourceName);
//Post the given resource
}
}
Now, the GET method works perfectly, when you go to
myUrl/person/personCode/resourceName, it gives me the correct resource.
The auth_token is used with every single call to the server (for now, authentication is done by comparing with a predefined string), so it's needed. All the other parameters are provided through the path, except for the authentication token, which should not be in the path as it does not relate to the identity of the required resource.
When I get to POST, it's a problem.
I know there's a way to tell the method it consumes a JSON, but in that case, what will happen to the other parameters (auth_token is one of them)?
Should I use Multipart?
Another related question, this is the first time I've designed such a server, is this design correct?
Thanks!
I am not sure I understand what you are trying to achieve. Let me try explain a few things - hope it will be relevant to your question:
#QueryParam injects parameters which are part of your path - i.e. the part of the URL that goes after "?".
E.g. if you have a URL like this:
http://yourserver.com/person/personCode/resourceName?resourceData=abc&token=1234
Then there would be 2 query params - one named resourceData with value "abc" and the other one named token with value "1234".
If you are passing an entity in the POST request, and that entity is of application/json type, you can simply annotate your post method using #Consumes("application/json") annotation and add another parameter to your method, which does not need to be annotated at all.
That parameter can be either a String (in that case Jersey would pass a raw JSON string and you would have to parse it yourself) or it can be a java bean annotated with #XmlRootElement annotation - in that case (if you also include jersey-json module on your classpath) Jersey will try to unmarshall the json string into that object using JAXB. You can also use Jackson or Jettison libraries to do that - see this section of Jersey User Guide for more info: http://jersey.java.net/nonav/documentation/latest/json.html
Found!
Client side:
Client c = Client.create();
WebResource service = c.resource("www.yourserver.com/");
String s = service.path("test/personCode/resourceName")
.queryParam("auth_token", "auth")
.type("text/plain")
.post(String.class, jsonString);
Server side:
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
#Path("/test/{personCode}/{resourceName}")
public class TestResourceProvider {
#POST
#Consumes("text/plain")
#Produces("application/json")
public String postUserResource(String jsonString,
#PathParam("personCode") String personCode,
#PathParam("resourceName") String resourceName,
#QueryParam("auth_token") String auth_token)
throws UnhandledResourceException {
//Do whatever...
}
}
In my case, I will parse the json I get in the server depending on the resource name, but you can also pass the object itself, and make the server consume an "application/json".