Suppose there are two models User and City
#JsonSerializable()
class User {
int id;
String name;
City? city;
}
#JsonSerializable()
class City {
int id;
String name;
}
Now suppose during API call, we've got a user model but in the city object model, we only get id not name. Something like this
{
"id": 5,
"name": "Matthew",
"city": {
"id": 12
}
}
But due to the default nature of json_serializable and json_annotation.
This JSON is not mapped to the User model, during mapping, it throws the exception.
type Null is not a subtype of type String. (because here name key is missing in city object)
But as we already declared in the User object that City is optional, I wanted that it should parse the User JSON with city as null.
Any help or solution would be really appreciated, Thank you
There is currently no support for ignoring a certain field only while serializing or only while deserializing. You can either ignore both or none. However, there is a workaround that I use.
Make a global method in your model file that just returns null like this:
T? toNull<T>(_) => null;
Inside your User model add a custom JsonKey for City:
#JsonKey(fromJson: toNull, includeIfNull: false)
City? City;
What this does is when converting from Json it uses your specificed function for converting city and replaces your value with null. Then due to includeIfNull property it just skips parsing.
Related
I have an entity
class Data {
string name;
string city;
string street;
string phone;
string email;
}
An api has been written to find Data by each param. This is search api so if a param is provided, it will be used if not then everything has to be queried for that param.
#Query("{'name': ?0,'city': ?1,'street': ?2, 'phone': ?3,'email': ?4}")
Page<IcePack> findDataSearchParams(String name,
String city,
String street,
String phone,
String email);
This only works when all the params are sent in the request. It wont work if any of the params are not sent because it will look for null value in the DB for that param.
I want to query everything for that param if it is not requested like the way it is done in SQL. I tired to use $regex with empty string when something is not sent but regex works like a like search but I want to do equal search
anyway to do this
Filtering out parts of the query depending on the input value is not directly supported. Nevertheless it can be done using #Query the $and and operator and a bit of SpEL.
interface Repo extends CrudRepository<IcePack,...> {
#Query("""
{ $and : [
?#{T(com.example.Repo.QueryUtil).ifPresent([0], 'name')},
?#{T(com.example.Repo.QueryUtil).ifPresent([1], 'city')},
...
]}
""")
Page<IcePack> findDataSearchParams(String name, String city, ...)
class QueryUtil {
public static Document ifPresent(Object value, String property) {
if(value == null) {
return new Document("$expr", true); // always true
}
return new Document(property, value); // eq match
}
}
// ...
}
Instead of addressing the target function via the T(...) Type expression writing an EvaluationContextExtension (see: json spel for details) allows to get rid of repeating the type name over and over again.
I have two Database table. I want to get the values from those tables by mysql queries and pass it to the controller. There I bind it one struct and show the json values as response to the controller. But the problem is there are some uncommon columns between two table but I have to use every fields in common Struct. When I got the response for one table other table's field are showing empty in the json object and vice versa. How to show only the related fields in every json object?
Code:
Model:
type Student struct {
StudentId int
Name string
Address string
}
type Teacher struct {
TeacherId int
Name string
Address string
Department string
}
type TeacherStudent struct {
StudentId int
Name string
Address string
TeacherId int
Department string
}
Controller:
json.Unmarshal([]byte(datas), &ts)
c.Data["json"] = ts
c.ServeJSON()
Response in Postman:
[
{
"StudnetId": 501,
"Name": Mark,
"Address": Canbera,
"TeacherId": 0,
"Deparment": ""
},
{
"StudnetId": 0,
"Name": John,
"Address": Melbourne,
"TeacherId": 101,
"Deparment": "Science"
}]
For Studnet Response I Don't want to show the TeacherId and Department and For Teacher Record I don't want to show the StudentId in json Object.
The solution for this is using json:"omitempty"
Referring to this article: https://www.sohamkamani.com/golang/omitempty/
Note: json:"omitempty" works for the fields only. It will not work for the structs inside the structs. Also it will not work for the time.Time fields. Referring to this article to handle the time.Time issue: JSON omitempty With time.Time Field
I have a problem,
I have a simple model class, that has few variables, but, one of them is not necessarily present all the time.
but JsonSerializable doesn't convert json to model object if that one key is not available in json data.
Sample code -
import "package:json_annotation/json_annotation.dart";
part "address.g.dart";
#JsonSerializable()
class Address {
final String country_code, state, city, street_line1, street_line2, post_code;
Address(this.country_code, this.state, this.city, this.street_line1,
this.street_line2, this.post_code);
factory Address.fromJson(Map<String, dynamic> json) =>
_$AddressFromJson(json);
Map<String, dynamic> toJson() => _$AddressToJson(this);
}
So as you can see, it is a model class, but street_line2 is not always present. and json serializable doesnot work if it's not present.
use "?" for nullable variables in your model
like this: String? then it can be null
I had a doubt like how to handle field does not exists situation like suppose i have released my app and in future updates i added a new field in doc then how can i handle if field does not exists.
For example, in shared preferences we use ??to handle data existence with the specified key value.
int val=prefs.getInt("myKey")??0;
as you can see that above code will set value of val to 0 if there's no value associated with the key- myKey. Similarly i would like to know is there any way of doing it for firestore document fields.
MyCode:-
class UserModel
{
final String? id;
final String? username;
final String? email;
UserModel({
this.id,
this.username,
this.email,
});
factory UserModel.fromDocument(DocumentSnapshot doc)
{
return UserModel(
id: doc['id'],
username: doc['username'],//suppose the username does not exist in the field then how can i assign the value "User" to the username?
email: doc['email'],
);
}
}
This is the problem when developing an app which use Firebase. If you add a field after there is already a data and if you try to get new field in dart, you are getting this error. I think the only solution is deleting all the old data which doesn't have new field. Or put this new field to all of your old data. It's kinda annoying. So make sure that you have to add all possible field in the beginning.
Unfortunately, you can't use like int val=prefs.getInt("myKey")??0; because it isn't even null.
I have two domain objects,
#Document
public class PracticeQuestion {
private int userId;
private List<Question> questions;
// Getters and setters
}
#Document
public class Question {
private int questionID;
private String type;
// Getters and setters
}
My JSON doc is like this,
{
"_id" : ObjectId("506d9c0ce4b005cb478c2e97"),
"userId" : 1,
"questions" : [
{
"questionID" : 1,
"type" : "optional"
},
{
"questionID" : 3,
"type" : "mandatory"
}
]
}
I have to update the "type" based on userId and questionId, so I have written a findBy query method inside the custom Repository interface,
public interface CustomRepository extends MongoRepository<PracticeQuestion, String> {
List<PracticeQuestion> findByUserIdAndQuestionsQuestionID(int userId,int questionID);
}
My problem is when I execute this method with userId as 1 and questionID as 3, it returns the entire questions list irrespective of the questionID. Is the query method name valid or how should I write the query for nested objects.
Thanks for any suggestion.
Just use the #Query annotation on that method.
public interface CustomRepository extends MongoRepository<PracticeQuestion, String> {
#Query(value = "{ 'userId' : ?0, 'questions.questionID' : ?1 }", fields = "{ 'questions.questionID' : 1 }")
List<PracticeQuestion> findByUserIdAndQuestionsQuestionID(int userId, int questionID);
}
By adding the fields part of the #Query annotation, you are telling Mongo to only return that part of the document. Beware though, it still returns the entire document in the same format - just missing everything you did not specify. So your code will still have to return List<PracticeQuestion> and you will have to do:
foreach (PracticeQuestion pq : practiceQuestions) {
Question q = pq.getQuestions().get(0); // This should be your question.
}
Property expressions
Property expressions can refer only to a direct property of the managed entity, as shown in the preceding example. At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties. Assume Persons have Addresses with ZipCodes. In that case a method name of List<Person> findByAddressZipCode(ZipCode zipCode);
creates the property traversal x.address.zipCode. The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized). If the algorithm succeeds it uses that property. If not, the algorithm splits up the source at the camel case parts from the right side into a head and a tail and tries to find the corresponding property, in our example, AddressZip and Code. If the algorithm finds a property with that head it takes the tail and continue building the tree down from there, splitting the tail up in the way just described. If the first split does not match, the algorithm move the split point to the left (Address, ZipCode) and continues.
Although this should work for most cases, it is possible for the algorithm to select the wrong property. Suppose the Person class has an addressZip property as well. The algorithm would match in the first split round already and essentially choose the wrong property and finally fail (as the type of addressZip probably has no code property). To resolve this ambiguity you can use _ inside your method name to manually define traversal points. So our method name would end up like so:
UserDataRepository:
List<UserData> findByAddress_ZipCode(ZipCode zipCode);
UserData findByUserId(String userId);
ProfileRepository:
Profile findByProfileId(String profileId);
UserDataRepositoryImpl:
UserData userData = userDateRepository.findByUserId(userId);
Profile profile = profileRepository.findByProfileId(userData.getProfileId());
userData.setProfile(profile);
Sample Pojo :
public class UserData {
private String userId;
private String status;
private Address address;
private String profileId;
//New Property
private Profile profile;
//TODO:setter & getter
}
public class Profile {
private String email;
private String profileId;
}
For the above Document/POJO in your Repository Class:
UserData findByProfile_Email(String email);
For ref : http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
You need to use Mongo Aggregation framework :
1) Create custom method for mongo repository : Add custom method to Repository
UnwindOperation unwind = Aggregation.unwind("questions");
MatchOperation match = Aggregation.match(Criteria.where("userId").is(userId).and("questions.questionId").is(questionID));
Aggregation aggregation = Aggregation.newAggregation(unwind,match);
AggregationResults<PracticeQuestionUnwind> results = mongoOperations.aggregate(aggregation, "PracticeQuestion",
PracticeQuestionUnwind.class);
return results.getMappedResults();
2) You need to cretae a class(Because unwind operation has changed the class structure) like below :
public class PracticeQuestionUnwind {
private String userId;
private Question questions;
This will give you only those result which matches the provide userId and questionId
Result for userId: 1 and questionId : 111 :
{
"userId": "1",
"questions": {
"questionId": "111",
"type": "optional"
}
}
i too had similar issue. for that i added $ before the nested class attributes.
try below query
#Query(value = "{ 'userId' : ?0, 'questions.$questionID' : ?1 }") List<PracticeQuestion> findPracticeQuestionByUserIdAndQuestionsQuestionID(int userId, int questionID);