so I have this one problem with Gson I just cannot figure out.
Imagine a Json structure for handling tickets. The Json looks like this:
{
"concertTicket" : { ... some valid json object ... }
}
I wrote my parser like this:
#SerializedName("concertTicket")
JsonObject ticketContent;
This is simple. Now I have a JsonObject called ticketContent that stores the ticket related stuff.
So far so good... now there are exactly three different kinds of tickets: let's assume concertTicket, planeTicket and museumTicket.
What I want to achieve is a Parser with two variables. One storing the ticketType and another one storing the ticketContents.
I figured out the parser for the ticketContent:
#SerializedName(value="concertTicket", alternate={"planeTicket", "museumTicket"})
JsonObject ticketContent;
My question is now: Is it possible to write an annotation that returns the Json-Key as String in a variable called ticketType? Like so:
# ... what to put here ? ...
String ticketType;
So that
if its a concertTicket, the value of ticketType = "concertTicket",
if its a planeTicket, the value of ticketType = "planeTicket" and
if its a museumTicket, the value of ticketType = "museumTicket"?
--- Here is my workaround ---
#SerializedName( "concertTicket" )
private JsonObject concertTicket;
#SerializedName( "planeTicket" )
private JsonObject planeTicket;
#SerializedName( "museumTicket" )
private JsonObject museumTicket;
public enum TicketType {
concertTicket, planeTicket, museumTicket
}
/*
* since only one of the TicketTypes can be not null, return this as TicketType
*/
public TicketType getTicketType() {
if ( concertTicket != null ) {
return TicketType.concertTicket;
}
if ( planeTicket != null ) {
return TicketType.planeTicket;
}
if ( museumTicket != null ) {
return TicketType.museumTicket;
}
return null;
}
/*
* since only one of the TicketTypes can be not null, return this as content
*/
public JsonObject getTicketContent() {
if ( concertTicket != null ) {
return concertTicket;
}
if ( planeTicket != null ) {
return planeTicket;
}
if ( museumTicket != null ) {
return museumTicket;
}
return null;
}
But I'm wondering if there is a more elegant solution to this?
If you have 3 tickets, lets say:
concertTicket, planeTicket and museumTicket
then the should have a common parent/super class in common, or an Interface...
if so; then you can deserialize to the interface and then cast to the ticket type..
Example:
interface Iticket {
void printTicket();
}
public class Ticket implements Iticket {
protected String id;
protected int price;
public static void main(String[] args) {
String ticketString = "{\"id\":\"0000\", \"price\":15}";
Gson g = new Gson();
Ticket c = g.fromJson(ticketString, Concert.class);
((Concert) c).jump();
Ticket m = g.fromJson(ticketString, Museum.class);
((Museum) m).niddle();
}
#Override
public void printTicket() {
// TODO Auto-generated method stub
}
}
class Concert extends Ticket implements Iticket {
#Override
public void printTicket() {
//
System.out.println("Loud printer");
}
public void jump() {
System.out.println("jump");
}
}
class Museum extends Ticket implements Iticket {
#Override
public void printTicket() {
System.out.println("Boring printer");
}
public void niddle() {
System.out.println("niddle");
}
}
Related
I have below table
question_text_id question_id language_id question_text
2 7 1 english_text_1
3 7 2 spanish_text_1
4 8 2 spanish_text_2
5 8 1 english_text_2
NOw i want to create list for each distinct question_id
i have used below code
List<QuestionText> questionTextList = questionManager.getQuestionsTextByQuestionId(Long.parseLong(questions.getQuestionId().toString()));
for (QuestionText questionText : questionTextList) {
questionMap.put("questionId", questionText.getQuestionId());
questionMap.put("language", questionText.getLanguageId());
if(questionText.getLanguageId().longValue() == 1){
questionMap.put("englishQuestionText",questionText.getQuestionText());
} else {
questionMap.put("spanishQuestionText",questionText.getQuestionText());
}
questionListMap.add(questionMap);
}
adminCollectionBookendModel.put("questionListMap",questionListMap);
[{questionId = 1,language=1, englishQuestionText = english_text_1,spanishQuestionText=spanish_text_1},{questionId = 1,language=2, englishQuestionText = english_text_1,spanishQuestionText=spanish_text_1}]
But this give me repeatetion of object of same data if i have both spanish and english question text as shown above. How to get unique list?
How to get both spanish text and english text for each question_id along with language_id and to access it?
Please help me on this
The first step would be to create a POJO Class like this,
public class QuestionDetails {
private int questionId;
private int englishLanguageId;
private int spanishLanguageId;
private String englishLanguageText;
private String spanishLanguageText;
public int getQuestionId() {
return questionId;
}
public void setQuestionId(int questionId) {
this.questionId = questionId;
}
public int getEnglishLanguageId() {
return englishLanguageId;
}
public void setEnglishLanguageId(int englishLanguageId) {
this.englishLanguageId = englishLanguageId;
}
public int getSpanishLanguageId() {
return spanishLanguageId;
}
public void setSpanishLanguageId(int spanishLanguageId) {
this.spanishLanguageId = spanishLanguageId;
}
public String getEnglishLanguageText() {
return englishLanguageText;
}
public void setEnglishLanguageText(String englishLanguageText) {
this.englishLanguageText = englishLanguageText;
}
public String getSpanishLanguageText() {
return spanishLanguageText;
}
public void setSpanishLanguageText(String spanishLanguageText) {
this.spanishLanguageText = spanishLanguageText;
}
#Override
public String toString() {
return new StringBuilder().append("questionId: ").append(questionId)
.append(" ,englishLanguageId: ").append(englishLanguageId)
.append(" ,englishLanguageText: ").append(englishLanguageText)
.append(" ,spanishLanguageId: ").append(spanishLanguageId)
.append(" ,spanishLanguageText: ").append(spanishLanguageText)
.toString();
}
}
Next step would be to change your code snippet like this:
List<QuestionDetails> questionsList = new ArrayList<>();
List<QuestionText> questionTextList = questionManager
.getQuestionsTextByQuestionId(Long.parseLong(questions
.getQuestionId().toString()));
for (QuestionText questionText : questionTextList) {
/* Get QuestionDetails Object */
QuestionDetails qd = getQuestionDetails(
questionText.getQuestionId(), questionsList);
/* Check Null */
if(null == qd) {
/* Get New Object */
qd = new QuestionDetails();
/* Add Object To List */
questionsList.add(qd);
}
/* Set Question ID */
qd.setQuestionId(questionText.getQuestionId());
/* Set Language ID & Text */
if (questionText.getLanguageId().longValue() == 1) {
qd.setEnglishLanguageId(questionText.getLanguageId()
.longValue());
qd.setEnglishLanguageText(questionText.getQuestionText());
} else {
qd.setSpanishLanguageId(questionText.getLanguageId()
.longValue());
qd.setSpanishLanguageText(questionText.getQuestionText());
}
}
adminCollectionBookendModel.put("questionListMap", questionsList);
Finally, here is the implementation of the getQuestionDetails function:
private QuestionDetails getQuestionDetails(int questionId,
List<QuestionDetails> questionsList) {
/* Search Existing Object */
for (QuestionDetails qd : questionsList) {
/* Match Found */
if (qd.getQuestionId() == questionId) {
return qd;
}
}
/* No Object Found */
return null;
}
I would recommend doing the following:
To distinguish QuestionText, you need to override equals method using question_text_id. Otherwise two questions with the same question_id, but different language texts would be equal.
Then create two separate maps for each language. Then just iterate throug all questions and put each question in a corresponding map by question_id. You can retrive a question object by its question_id and get all necessary fields from it, including spanish / english text
List<QuestionText> questionTextList = questionManager.getQuestionsTextByQuestionId(Long.parseLong(questions.getQuestionTextId().toString()));
for (QuestionText questionText : questionTextList) {
if(questionText.getLanguageId().longValue() == 1){
englishQuestionMap.put("question_id",questionText);
} else {
spanishQuestionMap.put("question_id",questionText);
}
questionListMap.add(questionMap);
}
So, your maps will have type of Map<Long, QuestionText>
Even after some time trying to read and understand the topics already posted here, I am still confused on how to create events in Java.
Assuming that I have this class in C#:
public class HighlightsObjectHandler {
// Constants
private const String
JsonKeysHighlightsHolder = "Items",
JsonKeysHighlightUrl = "Url",
JsonKeysHighlightTranslationsHolder = "Traducoes",
JsonKeysHighlightTranslationLanguage = "Idioma",
JsonKeysHighlightTranslationText = "Titulo",
JsonKeysHighlightTranslationImage = "Imagem";
// Handlers
public event EventHandler HighlightsJsonChanged;
public event EventHandler HighlightsContentChanging;
public event EventHandler HighlightsContentChanged;
// Variables
private String
_json;
// Properties
public String HighlightsJson {
get {
return _json;
}
set {
if (value != _json && value != null) {
_json = value;
OnHighlightsJsonChanged( EventArgs.Empty );
ParseJson();
}
}
}
public Boolean HighlightsUpdating { get; private set; }
public List<HighlightObject> Highlights { get; private set; }
// Methods
private void ParseJson() {
JsonObject
jsonObject;
if (JsonObject.TryParse( HighlightsJson, out jsonObject )) {
OnHighlightsContentChanging( EventArgs.Empty );
// Json parsing and other stuff...
// ... it shouldn't matter for this question.
OnHighlightsContentChanged( EventArgs.Empty );
}
}
// Events
internal void OnHighlightsJsonChanged( EventArgs eventArgs ) {
if (HighlightsJsonChanged != null) {
HighlightsJsonChanged( this, eventArgs );
}
}
internal void OnHighlightsContentChanging( EventArgs eventArgs ) {
HighlightsUpdating = true;
if (HighlightsContentChanging != null) {
HighlightsContentChanging( this, eventArgs );
}
}
internal void OnHighlightsContentChanged( EventArgs eventArgs ) {
HighlightsUpdating = false;
if (HighlightsContentChanged != null) {
HighlightsContentChanged( this, eventArgs );
}
}
// Constructors
public HighlightsObjectHandler() {
Highlights = new List<HighlightObject>();
}
}
How would I make a copy of this in Java?
I somewhat understand that I need to create an interface that would hold the 3 EventHandlers that I have in this code. Then, I would have to implement that interface in the class. Let's assume that the class would have the exact same name and the result would be something like this:
public class HighlightsObjectHandler implements SomeListener { ... }
But, from what I see from tutorials and forums, they would fire, for instance, the HighlightsContentChanging directly instead of calling the OnHighlightsContentChanging ( where I would like to set a variable - HighlightsUpdating - to a value and then calling the listeners associated with the event ).
And there is where I'm losing my mind. How would I make this happen? In the Windows Phone app, that variable would help me whenever a page that had this content in it to set the page as loading or to display a message if the page has nothing to show.
UPDATE:
I've managed to create the code I as able to, or had acknowledge to. I'll leave here the code so far:
package com.example.nlsonmartins.myapplication.Highlights;
import java.util.ArrayList;
import org.json.*;
public class HighlightsObjectHandler {
// Constants
private final String
JsonKeysHighlightsHolder = "Items",
JsonKeysHighlightUrl = "Url",
JsonKeysHighlightTranslationsHolder = "Traducoes",
JsonKeysHighlightTranslationLanguage = "Idioma",
JsonKeysHighlightTranslationText = "Titulo",
JsonKeysHighlightTranslationImage = "Imagem";
// Enumerators
// Handlers
// Variables
private String
_json;
private Boolean
_updating;
private ArrayList<HighlightObject>
_highlights;
// Properties
public String HighlightsJson() {
return _json;
}
public void HighlightsJson(String highlightsJson) {
// Validate the json. This cannot be null nor equal to the present one ( to prevent firing events on the same data )
if(highlightsJson != _json && highlightsJson != null) {
_json = highlightsJson;
// Fire the Java equivalent of C# 'OnHighlightsJsonChanged( EventArgs.Empty );'
ParseJson();
}
}
public Boolean HighlightsUpdating() {
return _updating;
}
private void HighlightsUpdating(Boolean isUpdating) {
_updating = isUpdating;
}
public ArrayList<HighlightObject> Highlights() {
return _highlights;
}
// Methods
private void ParseJson() {
try {
JSONObject
jsonObject = new JSONObject(HighlightsJson());
// Fire the Java equivalent of C# 'OnHighlightsContentsChanging( EventArgs.Empty );'
// Parse the JSON object
// Fire the Java equivalent of C# 'OnHighlightsContentsChanged( EventArgs.Empty );'
} catch (JSONException exception) {
}
}
// Events
/* Create the event handler for 'OnHighlightsJsonChanged' */
/* Create the event handler for 'OnHighlightsContentsChanging' and call the 'HighlightsUpdating(true);' method */
/* Create the event handler for 'OnHighlightsContentsChanged' and call the 'HighlightsUpdating(false);' method */
// Constructors
public HighlightsObjectHandler() {
_highlights = new ArrayList<HighlightObject>();
}
}
I don't have an equivalent for the 'JsonObject' type, but other than that I think the following may work for you, using your own custom EventHandler functional interface, custom EventArgs class, and generic 'Event' helper class:
import java.util.*;
public class HighlightsObjectHandler
{
// Constants
private static final String JsonKeysHighlightsHolder = "Items",
JsonKeysHighlightUrl = "Url",
JsonKeysHighlightTranslationsHolder = "Traducoes",
JsonKeysHighlightTranslationLanguage = "Idioma",
JsonKeysHighlightTranslationText = "Titulo",
JsonKeysHighlightTranslationImage = "Imagem";
// Handlers
public Event<CustomEventHandler> HighlightsJsonChanged = new Event<CustomEventHandler>();
public Event<CustomEventHandler> HighlightsContentChanging = new Event<CustomEventHandler>();
public Event<CustomEventHandler> HighlightsContentChanged = new Event<CustomEventHandler>();
// Variables
private String _json;
// Properties
public final String getHighlightsJson()
{
return _json;
}
public final void setHighlightsJson(String value)
{
if (!_json.equals(value) && value != null)
{
_json = value;
OnHighlightsJsonChanged(CustomEventArgs.Empty);
ParseJson();
}
}
private boolean HighlightsUpdating;
public final boolean getHighlightsUpdating()
{
return HighlightsUpdating;
}
private void setHighlightsUpdating(boolean value)
{
HighlightsUpdating = value;
}
private ArrayList<HighlightObject> Highlights;
public final ArrayList<HighlightObject> getHighlights()
{
return Highlights;
}
private void setHighlights(ArrayList<HighlightObject> value)
{
Highlights = value;
}
// Methods
private void ParseJson()
{
//todo: no equivalent to 'JsonObject':
JsonObject jsonObject = null;
//todo: no equivalent to 'out' parameter:
if (JsonObject.TryParse(HighlightsJson, jsonObject))
{
OnHighlightsContentChanging(CustomEventArgs.Empty);
// Json parsing and other stuff...
// ... it shouldn't matter for this question.
OnHighlightsContentChanged(CustomEventArgs.Empty);
}
}
// Events
public final void OnHighlightsJsonChanged(CustomEventArgs eventArgs)
{
if (HighlightsJsonChanged != null)
{
for (CustomEventHandler listener : HighlightsJsonChanged.listeners())
{
listener.invoke(this, eventArgs);
}
}
}
public final void OnHighlightsContentChanging(CustomEventArgs eventArgs)
{
setHighlightsUpdating(true);
if (HighlightsContentChanging != null)
{
for (CustomEventHandler listener : HighlightsContentChanging.listeners())
{
listener.invoke(this, eventArgs);
}
}
}
public final void OnHighlightsContentChanged(CustomEventArgs eventArgs)
{
setHighlightsUpdating(false);
if (HighlightsContentChanged != null)
{
for (CustomEventHandler listener : HighlightsContentChanged.listeners())
{
listener.invoke(this, eventArgs);
}
}
}
// Constructors
public HighlightsObjectHandler()
{
setHighlights(new ArrayList<HighlightObject>());
}
}
#FunctionalInterface
public interface CustomEventHandler
{
void invoke(object sender, CustomEventArgs e);
}
public class CustomEventArgs
{
public static readonly CustomEventArgs Empty;
public CustomEventArgs()
{
}
}
//this is produced as a helper class by C# to Java Converter:
public final class Event<T>
{
private java.util.Map<String, T> namedListeners = new java.util.HashMap<String, T>();
public void addListener(String methodName, T namedEventHandlerMethod)
{
if (!namedListeners.containsKey(methodName))
namedListeners.put(methodName, namedEventHandlerMethod);
}
public void removeListener(String methodName)
{
if (namedListeners.containsKey(methodName))
namedListeners.remove(methodName);
}
private java.util.List<T> anonymousListeners = new java.util.ArrayList<T>();
public void addListener(T unnamedEventHandlerMethod)
{
anonymousListeners.add(unnamedEventHandlerMethod);
}
public java.util.List<T> listeners()
{
java.util.List<T> allListeners = new java.util.ArrayList<T>();
allListeners.addAll(namedListeners.values());
allListeners.addAll(anonymousListeners);
return allListeners;
}
}
import java.util.*;
interface HelloListener
{ void someoneSaidHello();}
NOTE
I'm going to have this StackOverflow answer as a base for what I'm going to explain in this answer.
Okay, so, for what I could read and understand, it isn't that much different building C#-like events in Java ( or, in another point of view, it isn't that hard from someone who develops in C# to build events in Java ).
First, from my perspective, I'd like to point that the way I build the events in Java are almost a copy-paste from C# ( maybe it's the correct way to do it, maybe it isn't ).
Second, I'm going to - hopefully - put this in a way people might understand ( based on tutorials I saw here on StackOverflow and other sites ):
The events on C# are wrapped in a method that is set as internal - usually the OnSomethingChanging or OnSomethingChanged - whereas the Java events are not. Imagine this method in Java:
List<HelloListener> listeners = new ArrayList<HelloListener>();
public void sayHello() {
System.out.println("Hello!!");
// Notify everybody that may be interested.
for (HelloListener hl : listeners)
hl.someoneSaidHello();
}
Now, to make it more C# like, I would to make it like this:
public event EventHandler HelloListener;
public void SayHello() {
Console.WriteLine("Hello!!");
// Notify everybody that may be interested.
if(HelloListener != null) {
HelloListener(this, EventArgs.Empty);
}
}
Basically I was expecting to have to make an OnHelloListener method, then trigger the events on that very method but, on the majority of examples and tutorials that I saw, they would do something like I wrote above. That was what was messing my head really badly ( and probably others too if they come from C# to Java ).
In conclusion
If I was to translate the HighlightsObjectHandler class from C# to Java - and keeping the C# soul in it - I would end with something like this:
public class HighlightsObjectHandler {
// Constants
private final String
JsonKeysHighlightsHolder = "Items",
JsonKeysHighlightUrl = "Url",
JsonKeysHighlightTranslationsHolder = "Traducoes",
JsonKeysHighlightTranslationLanguage = "Idioma",
JsonKeysHighlightTranslationText = "Titulo",
JsonKeysHighlightTranslationImage = "Imagem";
// Enumerators
// Handlers
private List<HighlightsListener>
_highlightsListeners = new ArrayList<HighlightsListener>();
// Variables
private String
_json;
private Boolean
_updating;
private List<HighlightObject>
_highlights;
// Properties
public String HighlightsJson() {
return _json;
}
public void HighlightsJson(String highlightsJson) {
// Validate the json. This cannot be null nor equal to the present one ( to prevent firing events on the same data )
if (!highlightsJson.equals(_json) && highlightsJson != null) {
_json = highlightsJson;
OnHighlightsJsonChanged();
ParseJson();
}
}
public Boolean HighlightsUpdating() {
return _updating;
}
private void HighlightsUpdating(Boolean isUpdating) {
_updating = isUpdating;
}
public List<HighlightObject> Highlights() {
return _highlights;
}
// Methods
private void ParseJson() {
if (HighlightsUpdating()) {
return;
}
try {
OnHighlightsContentsChanging();
// Parse the JSON object
OnHighlightsContentsChanged();
} catch (JSONException exception) {
}
}
// Events
private void OnHighlightsJsonChanged() {
for(HighlightsListener highlightsListener : _highlightsListeners) {
highlightsListener.HighlightsJsonChanged();
}
}
private void OnHighlightsContentsChanging() {
HighlightsUpdating(true);
for(HighlightsListener highlightsListener : _highlightsListeners) {
highlightsListener.HighlightsContentChanging();
}
}
private void OnHighlightsContentsChanged() {
HighlightsUpdating(false);
for(HighlightsListener highlightsListener : _highlightsListeners) {
highlightsListener.HighlightsContentChanged();
}
}
// Constructors
public HighlightsObjectHandler() {
_highlights = new List<HighlightObject>();
}
}
Once again, my problem was basically me expecting to have to create the OnSomethingChanged methods that would trigger the events and not the code directly placed on the methods when I want them to be triggered.
You could say that I was an app that was crashing when you typed this while expecting you to type that.
Java to C#
WARNING If you're easily confused or you're still trying to understand this, I recommend you to not read this part of the answer. This is just an for fun and curiosity block that I found somewhat funny and interesting...
So, let's say that my problem was the opposite that is now, I had a Java class with events and would like to translate it to C#. From what I know to this point I would end with something like this in C#:
public class HighlightsObjectHandler {
// Constants
private const String
JsonKeysHighlightsHolder = "Items",
JsonKeysHighlightUrl = "Url",
JsonKeysHighlightTranslationsHolder = "Traducoes",
JsonKeysHighlightTranslationLanguage = "Idioma",
JsonKeysHighlightTranslationText = "Titulo",
JsonKeysHighlightTranslationImage = "Imagem";
// Enumerators
// Handlers
public event EventHandler HighlightsJsonChanged;
public event EventHandler HighlightsContentChanging;
public event EventHandler HighlightsContentChanged;
// Variables
private String
_json;
// Properties
public String HighlightsJson {
get {
return _json;
}
set {
if (value != _json && value != null) {
_json = value;
if (HighlightsJsonChanged != null) {
HighlightsJsonChanged( this, eventArgs );
}
ParseJson();
}
}
}
public Boolean HighlightsUpdating { get; private set; }
public List<HighlightObject> Highlights { get; private set; }
// Methods
private void ParseJson() {
JsonObject
jsonObject;
if (JsonObject.TryParse( HighlightsJson, out jsonObject )) {
HighlightsUpdating = true;
if (HighlightsContentChanging != null) {
HighlightsContentChanging( this, eventArgs );
}
// Json parsing
HighlightsUpdating = false;
if (HighlightsContentChanged != null) {
HighlightsContentChanged( this, eventArgs );
}
}
}
// Events
// Constructors
public HighlightsObjectHandler() {
Highlights = new List<HighlightObject>();
}
}
Note how instead the OnHighlightsJsonChanged and the other internal methods are removed and, instead of having the code I had on the methods they are instead where I called the methods.
P.S.: I will mark this answer as the answer to the this question on the next Monday so I can see others answers and select one of them if they fit more as a final answer.
I am using #CascadeSave to save child object in separate collection.
My Document classes are :
public class FbUserProfile{
#Id
private long id;
#DBRef(lazy=true)
#CascadeSave()
private Set<FacebookFriend> friends;
#DBRef(lazy=true)
#CascadeSave()
private Set<FacebookFriendList> customFriendList;
}
public class FacebookFriend{
#Id
private long id;
private String name;
}
public class FacebookFriendList{
#Id
private long id;
private String name;
private String list_type;
}
I add some object in both friends,customFriendList.
and try to update fbUserProfile object using:
mongoTemplate.save(fbUserProfile);
note: fbUserProfile already exists in db. Now I am updating this
Error Message: Cannot perform cascade save on child object without id set
If I remove #CascadeSave. It works fine for me. How I can Cascade set objects.
I am also using #CascadeSave with other objects. Its working fine but they are not set object.
I found the same tutorials somewhere else: Baeldung's and JavaCodeGeeks (this is the one i've followed)
I've had that same problem, and I could solve it.
It happens when you try to persist a collection. It doesn't matter that the collection's items have the #Id, because the collection itself won't have it. I edited the code in the EventListener's onBeforeConvert to check if the field you're trying to CascadeSave is a collection (in my case a List). If it's a list, you just cycle through it checking each individual item for #Id and saving them.
If it's not a collection you still have to persist them the same way you did before
#Override
public void onBeforeConvert(Object source) {
ReflectionUtils.doWithFields(source.getClass(), new ReflectionUtils.FieldCallback() {
#Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.makeAccessible(field);
if (field.isAnnotationPresent(DBRef.class) && field.isAnnotationPresent(CascadeSave.class)){
final Object fieldValue = field.get(source);
if(fieldValue instanceof List<?>){
for (Object item : (List<?>)fieldValue){
checkNSave(item);
}
}else{
checkNSave(fieldValue);
}
}
}
});
}
private void checkNSave(Object fieldValue){
DbRefFieldCallback callback = new DbRefFieldCallback();
ReflectionUtils.doWithFields(fieldValue.getClass(), callback);
if (!callback.isIdFound()){
throw new MappingException("Oops, something went wrong. Child doesn't have #Id?");
}
mongoOperations.save(fieldValue);
}
The best way to set an ID on the dependent child object is to write a listener class by extending AbstractMongoEventListener class and override the onConvert() method.
public class CustomMongoEventListener extends
AbstractMongoEventListener<Object> {
#Autowired
private MongoOperations mongoOperations;
#Override
public void onBeforeConvert(final Object entity) {
if (entity.id == null || entity.id.isEmpty()) {
entity.id = generateGuid(); //generate random sequence ID
}
public static String generateGuid() {
SecureRandom randomGen = new SecureRandom();
byte[] byteArray = new byte[16];
randomGen.nextBytes(byteArray);
return new Base32().encodeToString(byteArray).substring(0,26);
}
}
Finally register your custom listener in `your configuration file. For annotation approach use the following code to register :
#Bean
public CustomMongoEventListener cascadingMongoEventListener() {
return new CustomMongoEventListener();
}
The above solution works fine incase if you have a list. But we can avoid firing a save query for each element from the list, as it reduces the performance. Here is the solution I have found out of the above code.
#Override
public void onBeforeConvert(BeforeConvertEvent<Object> event) {
Object source = event.getSource();
ReflectionUtils.doWithFields(source.getClass(), new ReflectionUtils.FieldCallback() {
#Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.makeAccessible(field);
if (field.isAnnotationPresent(DBRef.class) && field.isAnnotationPresent(CascadeSave.class)){
final Object fieldValue = field.get(source);
if(fieldValue instanceof List<?>){
for (Object item : (List<?>)fieldValue){
checkNAdd(item);
}
}else{
checkNAdd(fieldValue);
}
mongoOperations.insertAll(documents);
}
}
});
}
private void checkNAdd(Object fieldValue){
DbRefFieldCallback callback = new DbRefFieldCallback();
ReflectionUtils.doWithFields(fieldValue.getClass(), callback);
if (!callback.isIdFound()){
throw new MappingException("Oops, something went wrong. Child doesn't have #Id?");
}
documents.add(fieldValue);
}
Okey I extend the class and it will check if the document is exist if it exist it will update the document else it insert the document:
#Component
class GenericCascadeMongo(
private val mongoTemplate: MongoTemplate
) : AbstractMongoEventListener<Any>() {
override fun onBeforeConvert(event: BeforeConvertEvent<Any?>) {
val source = event.source
?: return
ReflectionUtils.doWithFields(source.javaClass) { field ->
ReflectionUtils.makeAccessible(field)
if (field.isAnnotationPresent(DBRef::class.java) && field.isAnnotationPresent(CascadeSave::class.java)) {
val fieldValue = field[source]
?: return#doWithFields
if (fieldValue is List<*>) {
fieldValue.filterNotNull().forEach {
checkAndSave(it)
}
} else {
checkAndSave(fieldValue)
}
}
}
}
private fun checkAndSave(fieldValue: Any) {
try {
val callback = DbRefFieldCallback(fieldValue)
ReflectionUtils.doWithFields(fieldValue.javaClass, callback)
if (!callback.isIdFound && callback.id == null) {
mongoTemplate.insert(fieldValue)
}
if (callback.id != null) {
val findById = mongoTemplate.exists(Query(Criteria.where(MConst.MONGO_ID).`is`(callback.id)), fieldValue.javaClass)
if (findById) {
mongoTemplate.save(fieldValue)
} else {
mongoTemplate.insert(fieldValue)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private class DbRefFieldCallback(val fieldValue: Any?) : FieldCallback {
var isIdFound = false
private set
var id: String? = null
private set
#Throws(IllegalArgumentException::class, IllegalAccessException::class)
override fun doWith(field: Field) {
ReflectionUtils.makeAccessible(field)
if (field.isAnnotationPresent(Id::class.java)) {
isIdFound = true
id = ReflectionUtils.getField(field, fieldValue)?.toString()
}
}
}
}
I am trying to use ROME to parse an RSS feed like this:
url = new URL("http://www.rssboard.org/files/sample-rss-2.xml");
XmlReader reader = new XmlReader(url);
SyndFeedInput input = new SyndFeedInput();
SyndFeed feed = input.build(reader);
System.out.println(feed.getAuthor());
However, I cannot find a method to get the "WebMaster" field or any other customized field.
I have read about the custom modules in Rome from here, but I couldn't figure out how to use it. I create a similar SamplleModule, SampleModuleImpl, and SampleModule Parser for webMaster field, but I don't know how to use it!
This the classes that I have implemented:
SamplleModule:
public interface SampleModule extends Module {
public static final String URI =
"http://www.rssboard.org/files/sample-rss-2.xml";
public String getWebMaster();
public void setWebMaster(String webMaster);
}
SampleModuleImpl:
public class SampleModuleImpl extends ModuleImpl implements SampleModule {
private static final long serialVersionUID = 1L;
private String _webMaster;
protected SampleModuleImpl() {
super(SampleModule.class, SampleModule.URI);
}
#Override
public void copyFrom(Object obj) {
SampleModule sm = (SampleModule) obj;
setWebMaster(sm.getWebMaster());
}
#Override
public Class getInterface() {
return SampleModule.class;
}
#Override
public String getWebMaster() {
return _webMaster;
}
#Override
public void setWebMaster(String webMaster) {
_webMaster = webMaster;
}
}
and SampleModuleParser:
public class SampleModuleParser implements ModuleParser {
private static final Namespace SAMPLE_NS = Namespace.getNamespace("sample",
SampleModule.URI);
#Override
public String getNamespaceUri() {
return SampleModule.URI;
}
#Override
public Module parse(Element dcRoot) {
boolean foundSomething = false;
SampleModule fm = new SampleModuleImpl();
Element e = dcRoot.getChild("webMaster");
if (e != null) {
foundSomething = true;
fm.setWebMaster(e.getText());
}
return (foundSomething) ? fm : null;
}
}
I have also added these module to rome.properties.
I just don't know how to use them in my reader method.
Any idea folks?
take a look here for an example of how do to this with the MRSS module:
http://ideas-and-code.blogspot.com/2009/07/media-rss-plugin-for-rome-howto.html
Basically you take a SyndEntry object and using the namespace for your module you get an instance of your module object from the entry if one exists, so in your case:
SampleModule myModule = (SampleModule)e.getModule( SampleModule.URI );
And then you can use it. I use groovy with rome for my parser and do things like this:
def mediaModule = entry.getModule("http://search.yahoo.com/mrss/")
if(mediaModule) {
mediaModule.getMediaGroups().each { group ->
group.contents.each { content ->
if(content.type != null && content.type.startsWith("image")) {
log.info "got an image"
String imgUrl = content.getReference().toString()
post.images.add(new MediaContent(type:'image',url:imgUrl))
}
}
}
}
HTH
I currently have a class that extends Thread. In that class i get the contents of a web page (which is just JSON data) and i parse that. It depends what JSON object i get, because that determines what actions i take or what View i have to show.
But the way i'm currently doing it is i check in one class for all possible JSON request and perform actions based on that.
Example, my class kinda looks like this:
public class Communicator extends Thread
{
Thread threadToInterrupt = null;
String URL = null;
public Houses ( String URL )
{
threadToInterrupt = Thread.currentThread();
setDaemon(true);
this.URL = URL;
}
public void run()
{
// Code to get the JSON from a web page
// Finally parse the result into a String
String page = sb.toString();
JSONObject jObject = new JSONObject(page);
if ( !jObject.isNull("house") )
{
// do alot of stuff
}
else if ( !jObject.isNull("somethingelse") )
{
// do alot of other stuff
}
}
}
As you can imagine, this class would soon be cluttered with alot of JSON checks and codes. This doesn't feel like it's the right way.
I thought, maybe it's better to pass in a callback method that gets invoked? So that i could change my class to something like this:
public class Communicator extends Thread
{
Thread threadToInterrupt = null;
String URL = null;
public Houses ( String URL, String JsonString, object CallbackMethod )
{
// ... code
}
public void run()
{
// ....
JSONObject jObject = new JSONObject(page);
if ( !jObject.isNull(this.JsonString) )
{
// THen call the CallbackMethod...
CallbackMethod ( jObject );
}
}
}
public class MyClass
{
public void MyFunc()
{
(new Communicator("http://url.tld", "House", this.MyCallback)).start();
}
public void MyCallback(JSONObject jObject)
{
// Then i can perform actions here...
}
}
Not sure if that is a good idea. But if so, how do i create a callback like in my example? Is that possible somehow?
You won't use a callback, but a handler object like MyJsonHandler:
public class MyClass
{
public void MyFunc()
{
(new Communicator("http://url.tld", "House", new MyJsonHandler())).start();
}
}
public class MyJsonHandler() {
public void handle(JsonObject jo) {
// ...
}
}
Or create a new MyJsonHandler just in place when you need:
public void run()
{
// ....
JSONObject jObject = new JSONObject(page);
if ( !jObject.isNull(this.JsonString) )
{
// THen call the CallbackMethod...
new MyJsonHandler().handle(jObject);
}
}