I have created a graph database as hierarchical tree as a result of converting a MySQL database in another Neo4j database. The transformation worked fine.
On the top level exist a node labeled as Root, on the second level are the countries labeled as Country. Every country has cities that is the third level labeled as City. And down for every city there are a subset of addresses labeled as Address. The relationships should be,
(root)-[:IS_ROOT]->(country)-[:HAS_CITY]->(city)-[:HAS_ADDRESS]->(address)
Later, using Spring Data Neo4j repositories, I tried to query the database with no result.
For the MySQL database I use JDBC, and for the Neo4j database I use Spring Data Neo4j as mentioned before.
This is the controller I use to create the Neo4j database,
#Controller("importer")
public class SakilaDbImportController {
private static final Logger logger = LoggerFactory.getLogger(SakilaDbImportController.class);
#Autowired
#Lazy
AddressNeoRepository addressRepo;
#Autowired
#Lazy
CityNeoRepository cityRepo;
#Autowired
#Lazy
CountryNeoRepository countryRepo;
#Autowired
#Lazy
RootRepository rootRepo;
#Autowired
Neo4jTemplate template;
#Autowired
SakilaDbApiClient client;
public SakilaDbImportController() {
}
#Transactional
public RootNeo createGraphDb() {
RootNeo root = doImportRoot();
return root;
}
#Transactional
public RootNeo importRoot() {
return doImportRoot();
}
private RootNeo doImportRoot() {
logger.debug("Importing root");
RootNeo root = new RootNeo("1", "Root");
root.addLabel("_Root");
//root.addLabel("Root");
System.out.println("root created " + root);
List<Country> data = client.readAllCountries();
if (data.isEmpty()) throw new RuntimeException("Data for Root not found.");
Map<CountryNeo, RoleIsRoot> roles = relateCountriesToRoot(root, data);
//template.save(root);
Set<CountryNeo> set = roles.keySet();
for(Iterator<CountryNeo> it = set.iterator(); it.hasNext();) {
root.isRoot(it.next());
}
rootRepo.save(root);
/*
for(RoleIsRoot role: roles){
template.save(role);
}
*/
return root;
}
private Map<CountryNeo, RoleIsRoot> relateCountriesToRoot(RootNeo root, List<Country> data) {
Map<CountryNeo, RoleIsRoot> roles = new HashMap<CountryNeo, RoleIsRoot>();
for (Country country : data) {
if(country.getCountry().equalsIgnoreCase("Canada"))
continue;
CountryNeo countryNeo = doImportCountryNeo(country);
RoleIsRoot role = root.isRoot(countryNeo, "IS_ROOT_OF");
System.out.println("RoleIsRoot: " + role);
roles.put(countryNeo, role);
}
return roles;
}
#Transactional
public CountryNeo importCountryNeo(Country country) {
return doImportCountryNeo(country);
}
private CountryNeo doImportCountryNeo(Country country) {
logger.debug("Importing countryNeo");
CountryNeo countryNeo = new CountryNeo(generateIndex(country.getCountryId()), country.getCountry());
countryNeo.addLabel("_Country");
//countryNeo.addLabel("Country");
System.out.println("new country: " + countryNeo);
List<City> data = client.readAllCitiesByCountry(country);
if (data.isEmpty()) throw new RuntimeException("Data for Country not found.");
Map<CityNeo, RoleHasCity> roles = relateCitiesToCountry(countryNeo, data);
Set<CityNeo> set = roles.keySet();
for(Iterator<CityNeo> it = set.iterator(); it.hasNext();) {
countryNeo.hasCity(it.next());
}
countryRepo.save(countryNeo);
/*
template.save(countryNeo);
for(RoleHasCity role: roles){
template.save(role);
}
*/
return countryNeo;
}
private Map<CityNeo, RoleHasCity> relateCitiesToCountry(CountryNeo countryNeo, List<City> data) {
Map<CityNeo, RoleHasCity> roles = new HashMap<CityNeo, RoleHasCity>();
for (City city : data) {
CityNeo cityNeo = doImportCityNeo(city);
RoleHasCity role = countryNeo.hasCity(cityNeo, "IS_CITY_FROM");
System.out.println("RoleHasCity: " + role);
roles.put(cityNeo, role);
}
return roles;
}
#Transactional
public CityNeo importCityNeo(City city) {
return doImportCityNeo(city);
}
private CityNeo doImportCityNeo(City city) {
logger.debug("Importing cityNeo");
CityNeo cityNeo = new CityNeo(generateIndex(city.getCityId()), city.getCity());
cityNeo.addLabel("_City");
//cityNeo.addLabel("City");
System.out.println("new city: " + cityNeo);
List<Address> data = client.readAllAddressesByCity(city);
if (data.isEmpty()) throw new RuntimeException("Data for City not found.");
Map<AddressNeo, RoleHasAddress> roles = relateAddressesToCity(cityNeo, data);
Set<AddressNeo> set = roles.keySet();
for(Iterator<AddressNeo> it = set.iterator(); it.hasNext();) {
cityNeo.hasAddress(it.next());
}
cityRepo.save(cityNeo);
/*
template.save(cityNeo);
for(RoleHasAddress role: roles){
template.save(role);
}
*/
return cityNeo;
}
private Map<AddressNeo, RoleHasAddress> relateAddressesToCity(CityNeo cityNeo, List<Address> data) {
Map<AddressNeo, RoleHasAddress> roles = new HashMap<AddressNeo, RoleHasAddress>();
for(Address address: data) {
AddressNeo addressNeo = doImportAddressNeo(address);
RoleHasAddress role = cityNeo.hasAddress(addressNeo, "IS_ADDRESS_IN");
System.out.println("RoleHasAddress: " + role);
roles.put(addressNeo, role);
}
return roles;
}
#Transactional
public AddressNeo importAddressNeo(Address address) {
return doImportAddressNeo(address);
}
private AddressNeo doImportAddressNeo(Address address) {
logger.debug("Importing addressNeo");
if (address == null) throw new RuntimeException("Address not found.");
AddressNeo addressNeo = new AddressNeo(generateIndex(address.getAddressId()), address.getAddress());
addressNeo.addLabel("_Address");
//addressNeo.addLabel("Address");
System.out.println("new address: " + addressNeo);
addressNeo.setPostalCode(address.getPostalCode());
addressRepo.save(addressNeo);
/*
template.save(addressNeo);
*/
return addressNeo;
}
private String generateIndex(int index) {
return String.valueOf(index);
}
}
And this is the service that, using the repositories, does the queries,
#Service
public class SakilaDbQueries {
#Autowired
#Lazy
AddressNeoRepository addressRepo;
#Autowired
#Lazy
CityNeoRepository cityRepo;
#Autowired
#Lazy
CountryNeoRepository countryRepo;
#Autowired
#Lazy
RootRepository rootRepo;
public SakilaDbQueries() {
}
public List<String> findAllCountryNames() {
CountryNeoData names = countryRepo.findAllCountryNames();
Collection<String> collection = names.getCountries();
List<String> list = new ArrayList<String>();
for(Iterator<String> it = collection.iterator(); it.hasNext(); ) {
list.add(it.next());
}
return list;
}
public List<CountryNeo> findAllCountries() {
Result<CountryNeo> result = countryRepo.findAll();
List<CountryNeo> list = new ArrayList<CountryNeo>();
for(Iterator<CountryNeo> it = result.iterator(); it.hasNext(); ) {
list.add(it.next());
}
return list;
}
}
And as an example,
Here is the NodeEntity Place as generic one,
#NodeEntity
public class Place {
#GraphId Long nodeId;
#Indexed(unique=true)
String id;
#Indexed(indexType=IndexType.FULLTEXT, indexName = "place")
String name;
#Labels
private Collection<String> labels = Collections.emptySet();
protected Place(String id, String name) {
this.id = id;
this.name = name;
}
protected Place() {
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addLabel(String label) {
HashSet<String> newLabels = new HashSet<>(this.labels);
if (newLabels.add(label)) this.labels = newLabels;
}
public void removeLabel(String label) {
HashSet<String> newLabels = new HashSet<>(this.labels);
if (newLabels.remove(label)) this.labels = newLabels;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Place place = (Place) o;
if (nodeId == null) return super.equals(o);
return nodeId.equals(place.nodeId);
}
#Override
public int hashCode() {
return nodeId != null ? nodeId.hashCode() : super.hashCode();
}
#Override
public String toString() {
return String.format("%s [%s]", name, id);
}
}
and CountryNeo that extends Place,
#NodeEntity
public class CountryNeo extends Place {
#RelatedTo(type = "HAS_CITY", direction = Direction.OUTGOING)
Set<CityNeo> cities;
#Fetch #RelatedToVia(type = "IS_ROOT", direction = Direction.INCOMING)
Iterable<RoleIsRoot> roles;
public CountryNeo() {
}
public CountryNeo(String id, String name) {
super(id, name);
}
public Set<CityNeo> getCities() {
return cities;
}
public void hasCity(CityNeo city) {
if(cities == null)
cities = new HashSet<CityNeo>();
cities.add(city);
}
public RoleHasCity hasCity(CityNeo city, String roleName) {
return new RoleHasCity(this, city, roleName);
}
public Collection<RoleIsRoot> getRoles() {
Iterable<RoleIsRoot> allRoles = roles;
return allRoles == null ? Collections.<RoleIsRoot>emptyList() : IteratorUtil.asCollection(allRoles);
}
}
The used roles,
#RelationshipEntity(type = "HAS_CITY")
public class RoleHasCity {
#GraphId Long id;
#StartNode CountryNeo country;
#EndNode CityNeo city;
String name;
public RoleHasCity() {
}
public RoleHasCity(CountryNeo country, CityNeo city, String roleName) {
this.country = country;
this.city = city;
this.name = roleName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CountryNeo getCountry() {
return country;
}
public void setCountry(CountryNeo country) {
this.country = country;
}
public CityNeo getCity() {
return city;
}
public void setCity(CityNeo city) {
this.city = city;
}
#Override
public String toString() {
return String.format("%s %s %s", city, name, country);
}
}
And the repository,
public interface CountryNeoRepository extends GraphRepository<CountryNeo> {
#Query("MATCH (n:CountryNeo) RETURN COLLECT(n.name) AS countries")
CountryNeoData findAllCountryNames();
#QueryResult
public class CountryNeoData {
Collection<String> countries = Collections.emptyList();
public Collection<String> getCountries() {
return countries;
}
}
}
And here is the main bean,
public class SakilaImporter {
private GraphDatabaseService graphDatabase;
private SakilaDbImportController importer;
private SakilaDbQueries queries;
public SakilaImporter(GraphDatabaseService graphDatabase,
SakilaDbImportController importer,
SakilaDbQueries queries) {
this.graphDatabase = graphDatabase;
this.importer = importer;
this.queries = queries;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
GraphDatabaseService graphDatabase = ctx.getBean("graphDatabaseService", GraphDatabaseService.class);
SakilaDbImportController importer = ctx.getBean("importer", SakilaDbImportController.class);
SakilaDbQueries queries = ctx.getBean("queries", SakilaDbQueries.class);
SakilaImporter sakilaImporter = new SakilaImporter(graphDatabase, importer, queries);
Transaction tx = graphDatabase.beginTx();
try {
sakilaImporter.createDatabase();
sakilaImporter.queryDatabase();
tx.success();
} finally {
tx.close();
}
ctx.close();
}
public void createDatabase() {
System.out.println("Importing data...");
final long start = System.currentTimeMillis();
RootNeo root = importer.createGraphDb();
if(root != null) {
final long time = System.currentTimeMillis() - start;
System.out.println("Import places took " + time + " ms.");
} else {
System.out.println("root: " + root);
}
}
public void queryDatabase() {
System.out.println();System.out.println();
System.out.println("List of countries.-");
List<CountryNeo> countries = queries.findAllCountries();
for(CountryNeo country: countries) {
System.out.println(country);
}
System.out.println();System.out.println();
System.out.println("List of country names.-");
List<String> names = queries.findAllCountryNames();
for(String name: names) {
System.out.println(name);
}
}
}
The result of both queries is an empty list
spring 2.5.6
spring framework 4.0.0.RELEASE
neo4j 2.1.8
spring data neo4j 3.1.2.RELEASE
Related
Suppose that there are classes like:
CivilAddSystem class :
class CivilAddSystem{
List<People> people = new ArrayList<>();
List<Town> towns = new ArrayList<>();
public People addPeople(People people) {
people.add(people);
return people;
}
public Town addTown(Town town) {
towns.add(town);
return town;
}
public House addHouse (House house, String townName) throws IllegalStateException {
Town town = getTown(townName);
if (null == town) throw new IllegalStateException("No matching town");
town.addHouse(house);
return house;
}
public List<String> getTown(String name) {
for (Town town : towns) {
if (town.getName().equals(name)) return town;
}
return null;
}
}
Town class:
public class Town {
List<House> houses = new ArrayList<>();
String name;
String abbreviation;
public Town (String name, String abbreviation) {
this.name = name;
this.abbreviation = abbreviation;
}
public String getName() {
return name;
}
public String getAbbreviation() {
return abbreviation;
}
public void addHouse(House house) {
houses.add(house);
}
public House getHouse(String unitNumber) {
for (House house : houses) {
if (house.getUnitNumber().equals(unitNumber)) return house;
}
return null;
}
}
House class
public class House {
private final List<People> people = new ArrayList<>();
private final String unitNumber;
private final String houseName;
public House (String unitNumber, String houseName) {
this.unitNumber= unitNumber;
this.houseName= houseName;
}
public String getUnitNumber() {
return unitNumber;
}
public String getHouseName() {
return houseName;
}
private People checkMovedInPeople(People person) {
if (null == person) throw new NullPointerException();
for (People movedIn : people) {
if (movedIn.getName().equals(person.getName())) return movedIn;
if (movedIn.getPersonID().equals(person.getPersonID())) return movedIn;
}
return null;
}
public void moveInPeople(People person) throws IllegalArgumentException, IllegalStateException {
if (null == person) throw new IllegalArgumentException("Person shouldn't be null");
if (null != checkMovedInPeople(person)) throw new IllegalStateException("Person is already in the house");
people.add(person);
}
public List<String> getPeople() {
List<String> results = new ArrayList<>();
for (People person: people) {
results.add(person.getPersonID());
}
return results;
}
public People getPerson(String match) {
for (People person: people) {
if (person.getPersonID().equals(match)) return person;
if (person.getName().equals(match)) return person;
}
return null;
}
}
And for the people class, it just has two variables: String PersonID and String Name and their getter methods.
So, what I want to achieve is that, I am currently trying to test the addPeople method in CivilAddSystem class and I want to test it independently using mockito.
The test case I wrote using JUnit is like this:
#Test
public void testAddPeople() {
CivilAddSystem CAS = new CivilAddSystem();
Town town = CAS.addTown("BlueTown", "BT");
House house = CAS.addHouse("U180", "BlueHouse", "BlueTown");
People bob = CAS.addPeople("1", "Bob");
Assert.assertEquals(CAS.getTown("BlueTown").getHouse("U180").getPerson("1"), null);
house.moveInPeople(bob);
Assert.assertEquals(CAS.getTown("BlueTown").getHouse("U180").getPerson("1").getPersonID, 1);
}
But I am really struggling with applying mockito for this test case.
What I did so far is just mocking Town, House and People classes, (not the CivilAddSystem class since it is the one that is being tested), and stopped there...
Can anyone gives me a hint on how to apply mockito for that above test case?
p.s) A little bit of modification for the codes above is accepted (for example, applying dependency injection and something like this are accepted).
Thanks in advance!
I have been trying to get this over for a while now with little or no success. Right now, I am really out of options. I will appreciate some assistance or pointers towards the right direction.... since I believe I am not doing somethings very well.
After parsing with the code below, I have null values in most of the fields: Result{id=30c26c8a-8bdf-4d4d-8f8d-a19661f16877, name=Andriod_Office_Task, owner =generated.Owner#53d8d10a, comment=, creationTime=2016-09-09T19:30, modificationTime=2016-09-09T19:30:05+02:00, reportId=null, taskid=null, host=null, port=null, nvt=null, scanNVTVersion=null, threat=null, severity=null, description=null}
The parsing methods (other methods are excluded for brevity):
private List<Result> readDocument(XMLStreamReader parser) throws XMLStreamException, DatatypeConfigurationException {
List<Result> results = new ArrayList<>();
while (parser.hasNext()) {
int eventType = parser.next();
switch (eventType) {
case XMLStreamReader.START_ELEMENT:
String elementName = parser.getLocalName();
if (elementName.equals("result"))
results.add(readResult(parser));
break;
case XMLStreamReader.END_ELEMENT:
return results;
}
}
throw new XMLStreamException("Premature end of file");
}
public Result readResult(XMLStreamReader parser) throws XMLStreamException, DatatypeConfigurationException {
Result result = new Result();
result.setId(parser.getAttributeValue(null, "id"));
Report report = new Report();
Task task = new Task();
while (parser.hasNext()) {
int eventType = parser.next();
switch (eventType) {
case XMLStreamReader.START_ELEMENT:
String elementName = parser.getLocalName();
if (elementName.equals("name"))
result.setName(readCharacters(parser));
else if (elementName.equals("host"))
result.setHost(readCharacters(parser));
else if (elementName.equals("owner"))
result.setOwner(readOwner(parser));
else if (elementName.equals("comment"))
result.setComment(readCharacters(parser));
else if (elementName.equals("creation_time"))
result.setCreationTime(readCreationTime(parser));
else if (elementName.equals("modification_time"))
result.setModificationTime(readCharacters(parser));
else if (elementName.equals("report"))
report.setId(readReport(parser));
else if (elementName.equals("task"))
task.setId(readTask(parser));
else if (elementName.equals("user_tags"))
result.setUserTags(readUserTags(parser));
else if (elementName.equals("port"))
result.setPort(readCharacters(parser));
else if (elementName.equals("nvt"))
result.setNvt(readNvt(parser));
else if (elementName.equals("scan_nvt_version"))
result.setScanNVTVersion(readCharacters(parser));
else if (elementName.equals("threat"))
result.setThreat(readCharacters(parser));
else if (elementName.equals("severity"))
result.setSeverity(readCharacters(parser));
else if (elementName.equals("qod"))
result.setQod((Qod) readQod(parser));
else if (elementName.equals("description"))
result.setDescription(readCharacters(parser));
break;
case XMLStreamReader.END_ELEMENT:
}
return result;
}
throw new XMLStreamException("Premature end of file");
}
private String readCharacters(XMLStreamReader reader) throws XMLStreamException {
StringBuilder result = new StringBuilder();
while (reader.hasNext()) {
int eventType = reader.next();
switch (eventType) {
case XMLStreamReader.CHARACTERS:
result.append(reader.getText());
break;
case XMLStreamReader.END_ELEMENT:
return result.toString();
}
}
throw new XMLStreamException("Premature end of file");
}
}
The result class is below :
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Result {
#XmlAttribute
private String id;
#XmlElement
private String name;
#XmlElement
private Task task;
#XmlElement
private String comment;
#XmlElement(name = "creation_time")
String creationTime;
#XmlElement(name = "modification_time")
private String modificationTime;
// TODO user_tags
#XmlElement
private UserTags userTags;
#XmlElement
private Owner owner;
#XmlElement
private Qod qod;
/**
* // * The report the result belongs to (only when details were requested)
* //
*/
#XmlElementWrapper(name = "report")
#XmlElement(name = "reportId")
private String reportId;
#XmlElement
private String host;
#XmlElement
private String port;
#XmlElement
private NVT nvt;
#XmlElement(name = "scan_nvt_version")
private String scanNVTVersion;
#XmlElement
private String threat;
#XmlElement
private String severity;
#XmlElement
private String description;
public Result() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getCreationTime() {
return creationTime;
}
public void setCreationTime(String creationTime) {
this.creationTime = creationTime;
}
public String getModificationTime() {
return modificationTime;
}
public void setModificationTime(String modificationTime) {
this.modificationTime = modificationTime;
}
public UserTags getUserTags() {
return userTags;
}
public void setUserTags(UserTags userTags) {
this.userTags = userTags;
}
public Qod getQod() {
return qod;
}
public void setQod(Qod qod) {
this.qod = qod;
}
public Owner getOwner() {
return owner;
}
public void setOwner(Owner owner) {
this.owner = owner;
}
public String getReportId() {
return reportId;
}
public void setReportId(String reportId) {
this.reportId = reportId;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public NVT getNvt() {
return nvt;
}
public void setNvt(NVT nvt) {
this.nvt = nvt;
}
public String getScanNVTVersion() {
return scanNVTVersion;
}
public void setScanNVTVersion(String scanNVTVersion) {
this.scanNVTVersion = scanNVTVersion;
}
public String getThreat() {
return threat;
}
public void setThreat(String threat) {
this.threat = threat;
}
public String getSeverity() {
return severity;
}
public void setSeverity(String severity) {
this.severity = severity;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return "Result{" + "id=" + id +
", name=" + name + ", owner =" + owner +
", comment=" + comment + ", creationTime=" + creationTime + ", modificationTime=" + modificationTime
+ ", reportId=" + reportId + ", taskid=" + task + ", host=" + host + ", port=" + port + ", nvt=" + nvt
+ ", scanNVTVersion=" + scanNVTVersion + ", threat=" + threat + ", severity=" + severity
+ ", description=" + description + '}';
}
}
<get_results_response status="200" status_text="OK">
<result id="30c26c8a-8bdf-4d4d-8f8d-a19661f16877">
<name>Trace route</name>
<owner>
<name>admin</name>
</owner>
<comment/>
<creation_time>2016-09-09T19:30:05+02:00</creation_time>
<modification_time>2016-09-09T19:30:05+02:00</modification_time>
< id="2a6d7f75-f6b7-40b2-a792-b558fada375b"/>
<task id="e59ac66b-5b59-4756-bace-37bb1106276d">
<name>Andriod_Office_Task</name>
</task>
<user_tags>
<count>0</count>re
</user_tags>
<host>172.16.53.178</host>
<port>general/tcp</port>
<nvt oid="1.3.6.1.4.1.25623.1.0.51662">
<name>Traceroute</name>
<family>General</family>
<cvss_base>0.0</cvss_base>
<cve>NOCVE</cve>
<bid>NOBID</bid>
<xref>NOXREF</xref>
<tags>cvss_base_vector=AV:N/AC:L/Au:N/C:N/I:N/A:N|qod_type=remote_banner|solution=Block unwanted packets from escaping your network.|summary=A traceroute from the scanning server to the target system was
conducted. This traceroute is provided primarily for informational
value only. In the vast majority of cases, it does not represent a
vulnerability. However, if the displayed traceroute contains any
private addresses that should not have been publicly visible, then you
have an issue you need to correct.</tags>
<cert/>
</nvt>
<scan_nvt_version>$Revision: 2837 $</scan_nvt_version>
<threat>Log</threat>
<severity>0.0</severity>
<qod>
<value>80</value>
<type>remote_banner</type>
</qod>
<description>Here is the route from 192.168.14.128 to 172.16.53.178:
192.168.14.128
172.16.53.178</description>
</result>
<filters id="">
<term>first=1 rows=-1 sort=name</term>
<keywords>
<keyword>
<column>first</column>
<relation>=</relation>
<value>1</value>
</keyword>
<keyword>
<column>rows</column>
<relation>=</relation>
<value>-1</value>
</keyword>
<keyword>
<column>sort</column>
<relation>=</relation>
<value>name</value>
</keyword>
</keywords>
</filters>
<sort>
<field>name
<order>ascending</order></field>
</sort>
<results max="-1" start="1"/>
<result_count>3444
<filtered>1</filtered>
<page>1</page></result_count>
</get_results_response>
After some research and attempts with some common xml parsing approaches, I ended up using jackson-dataformat-xml approach. While this might not be the best it gave me what I wanted with much less code. Basically, I had to adapt the annotations in the model classes as below :
#JsonIgnoreProperties(ignoreUnknown=true)
#JacksonXmlRootElement(localName = "results")
public class Results {
#JacksonXmlProperty(localName = "result")
#JacksonXmlElementWrapper(useWrapping = false)
public Result [] result;
public Results() {
}
public Result[] getResult() {
return result;
}
public void setResult(Result[] result) {
this.result = result;
}
#Override
public String toString() {
return "Results [result=" + Arrays.toString(result) + "]";
}
And some adaptations for the parsing class:
public class GetReportsResponseHandler extends DefaultHandler<GetReportsResponse> {
private XmlMapper mapper = new XmlMapper();
public GetReportsResponseHandler() {
super(new GetReportsResponse(), "get_reports_response");
AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
AnnotationIntrospector secondary = new JaxbAnnotationIntrospector();
AnnotationIntrospector pair = new AnnotationIntrospectorPair(primary, secondary);
mapper.setAnnotationIntrospector(pair);
}
#Override
protected void parseStartElement(XMLStreamReader parser) throws XMLStreamException, IOException {
if ("report".equals(parser.getName().toString())){
Report report = mapper.readValue(parser, Report.class);
response.addReport(report);
}
I Have this Model :
1) Team :
public class Team {
private String nom ;
private Employee leader ;
private List<Employee> members = new ArrayList<Employee>();
private List<Person> persons = new ArrayList<Person>();
public Team() {
}
public Team(String nom, Employee leader, List<Employee> members, List<Person> persons) {
this.nom = nom;
this.leader = leader;
this.members = members;
this.persons = persons;
}
public Team(String nom) {
this.nom = nom;
}
public Employee addEmployee (Employee employee) {
members.add(employee);
return employee;
}
public Person addPerson (Person person) {
persons.add(person);
return person;
}
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public Employee getLeader() {
return leader;
}
public void setLeader(Employee leader) {
this.leader = leader;
}
public List<Employee> getMembers() {
return members;
}
public void setMembers(List<Employee> members) {
this.members = members;
}
#Override
public String toString() {
return "Team{" +
"nom='" + nom + '\'' +
", leader=" + leader +
", members=" + members +
'}';
}
public Team(int i){
this.setNom("team"+i);
this.setLeader(new Employee("MoMo" + i, "dd" + i));
this.addEmployee(new Employee("mehdi" + i, "dd" + i));
this.addEmployee(new Employee("albert" + i+1,"dd"+ i));
this.addEmployee(new Employee("lolo" + i+2,"dd"+ i));
this.addPerson(new Person("Person" + i));
this.addPerson(new Person("Person" + i+1 ));
this.addPerson(new Person("Person" + i+2 ));
}
2)
public class Employee {
private String cin;
private String nom;
private String prenom;
3)
public class Person {
private String address;
private String name;
public Person(){
}
public Person(String name) {
this.name = name;
}
public Person(String name, String address) {
this.name = name;
this.address = address;
}
public String getAddress() {
return address;
}
public String getName() {
return name;
}
}
I want to build a GWT TreeViewModel like the following :
Team => (i want to have my list of persons and my list of employees mixed)
Here is my TreeViewModel
public class EmployeeTreeModel implements TreeViewModel {
private final List<Team> teamList;
private final SingleSelectionModel<Employee> selectionModel
= new SingleSelectionModel<Employee>();
public EmployeeTreeModel() {
teamList = new ArrayList<Team>();
{
for (int i = 0; i < 5 ; i++) {
teamList.add(new Team(i) );
}
}
}
#Override
public <T> NodeInfo<?> getNodeInfo(T value) {
if (value == null) {
ListDataProvider<Team> dataProvider
= new ListDataProvider<Team>(teamList);
Cell<Team> cell = new AbstractCell<Team>() {
#Override
public void render(Context context, Team value, SafeHtmlBuilder sb) {
if (value != null) {
sb.appendHtmlConstant(" ");
sb.appendEscaped(value.getNom());
}
}
};
return new DefaultNodeInfo<Team>(dataProvider, cell);
}
else if (value instanceof Team) {
ListDataProvider<Employee> dataProvider
= new ListDataProvider<Employee>(
((Team) value).getMembers());
Cell<Employee> cell =
new AbstractCell<Employee>() {
#Override
public void render(Context context, Employee employee, SafeHtmlBuilder sb) {
if (employee != null) {
sb.appendHtmlConstant(" ");
sb.appendEscaped(employee.getNom());
}
}
};
return new DefaultNodeInfo<Employee>(dataProvider, cell,selectionModel, null);
}
return null;
}
#Override
public boolean isLeaf(Object o) {
if (o instanceof Employee) {
return true;
}else if(o instanceof Person){
return true;
}
else return false;
}
}
in my actual code I can't mix Employees data and Person's Data because two different objects
Any way to do that ?
A listDataProvider who brings two Objects maybe ?
I am using jackson library for deserializing json data.
Is there a way to skip some element if a attribute does not fit a criteria?
For Example
The java classes:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Group
{
private String name;
private int id;
private List<User> userList;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class User
{
private String firstName;
private String lastName;
private boolean deleted;
}
The Json File:
["test.Group", {
"name" : "testgroup1",
"id" : 3
"userList" : [ "java.util.ArrayList", [
["test.User", {
"firstName" : "John",
"lastName" : "Doe",
"deleted" : false } ],
["test.User", {
"firstName" : "John",
"lastName" : "Doe",
"deleted" : true } ],
["test.User", {
"firstName" : "John",
"lastName" : "Doe",
"deleted" : false } ] ] ]
}]
Usually I am deserialzing like this:
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
test.Group g1 = mapper.readValue(jsonString,test.Group.class);
Now, Is it possible to skip every user-element whose attribute "deleted" has value true ?
I there a way to do this with data-bind or do I have to use another method like tree or streaming ?
EDIT
I am developing for android, not desktop.
The reason for this question is, that there can be thousands of User elements and I want to minimize the memory usage.
Not skipping, but removing after reading using Java 8 (assuming your User has a getter for deleted):
g1.getUserList().removeIf(User::isDeleted);
You can inject an intermediary by parsing to a tree node and then filtering on the nodes. An example:
public static void main(String[] args)
{
Group g = new Group();
g.setId(1);
g.setName("Test");
User u1 = new User();
u1.setDeleted(false);
u1.setFirstName("John");
u1.setLastName("Jones");
User u2 = new User();
u2.setDeleted(true);
u2.setFirstName("Jane");
u2.setLastName("Jones");
g.addUser(u1);
g.addUser(u2);
try
{
ObjectMapper mapper = new ObjectMapper();
String jsonVal = mapper.writeValueAsString(g);
JsonNode node = mapper.readTree(jsonVal);
for (Iterator<Entry<String, JsonNode>> it = node.fields(); it.hasNext(); )
{
Map.Entry<String, JsonNode> field = it.next();
String key = field.getKey();
if ("userList".equals(key))
{
JsonNode users = field.getValue();
if (users.isArray())
{
for (Iterator<JsonNode> x = users.iterator(); x.hasNext();)
{
JsonNode entry = x.next();
if (entry.get("deleted").asBoolean())
{
System.out.println("Remove " + entry.get("firstName").asText() + " " + entry.get("lastName").asText());
x.remove();
}
else
{
System.out.println("Don't remove " + entry.get("firstName").asText() + " " + entry.get("lastName").asText());
}
}
}
}
}
Group grp = mapper.treeToValue(node, Group.class);
System.out.println("Final group: " + grp);
}
catch (Exception e)
{
System.out.println("Something went wrong...");
e.printStackTrace();
}
}
Output results in :
Don't remove John Jones
Remove Jane Jones
Final group: Group [name=Test, id=1, userList=[User [firstName=John, lastName=Jones, deleted=false]]]
Here is a second approach using a custom deserializer on the Group object. This is just something I read up on so there may be efficiencies that can be added:
public class Answer28536024 {
#JsonDeserialize(using = GroupDeserializer.class)
public static class Group
{
private String name;
private int id;
private List<User> userList;
public Group()
{
userList = new ArrayList<User>();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void addUser(User u)
{
userList.add(u);
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
#Override
public String toString() {
return "Group [name=" + name + ", id=" + id + ", userList=" + userList
+ "]";
}
}
public static class GroupDeserializer extends JsonDeserializer<Group>
{
#Override
public Group deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
Group group = new Group();
group.setName(node.get("name").asText());
group.setId(node.get("id").asInt());
JsonNode users = node.get("userList");
if (users.isArray())
{
for (JsonNode userNode : users)
{
if (!userNode.get("deleted").asBoolean())
{
User user = new User();
user.setFirstName(userNode.get("firstName").asText());
user.setLastName(userNode.get("lastName").asText());
user.setDeleted(false);
group.addUser(user);
}
}
}
return group;
}
}
public static class User
{
private String firstName;
private String lastName;
private boolean deleted;
public User()
{
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
#Override
public String toString() {
return "User [firstName=" + firstName + ", lastName=" + lastName
+ ", deleted=" + deleted + "]";
}
}
public static void main(String[] args)
{
Group g = new Group();
g.setId(1);
g.setName("Test");
User u1 = new User();
u1.setDeleted(false);
u1.setFirstName("John");
u1.setLastName("Jones");
User u2 = new User();
u2.setDeleted(true);
u2.setFirstName("Jane");
u2.setLastName("Jones");
g.addUser(u1);
g.addUser(u2);
try
{
ObjectMapper mapper = new ObjectMapper();
String jsonVal = mapper.writeValueAsString(g);
System.out.println(jsonVal);
Group grp = mapper.readValue(jsonVal, Group.class);
System.out.println("Final group: " + grp);
}
catch (Exception e)
{
System.out.println("Something went wrong...");
e.printStackTrace();
}
}
}
Output for this one:
{"name":"Test","id":1,"userList":[{"firstName":"John","lastName":"Jones","deleted":false},{"firstName":"Jane","lastName":"Jones","deleted":true}]}
Final group: Group [name=Test, id=1, userList=[User [firstName=John, lastName=Jones, deleted=false]]]
I am following those three tutorials and I have completed it with success.
http://oracleadfhowto.blogspot.in/2013/03/create-simple-web-service-using-oracle.html
http://oracleadfhowto.blogspot.in/2013/03/consuming-web-service-using-web-service.html
http://oracleadfmobile.blogspot.in/2013/03/consuming-soap-web-service-in-adf.html
But then, as author haven't implemented removeCountries method I tried to create it.
What I did initially was to just add to class Countries this method:
public boolean removeCountry(Country country) {
return countries.remove(country);
}
But although compiler wasn't complaining it didn't work. Actually it worked last night (before reboot) but not today.
Must be some SOAP iterator/bindig thing or whatever. Or I thought that it worked but in fact it didn't.
Here are original classes:
//-------------------------------
public class Country {
String CountryId;
String CountryName;
public Country() {
super();
}
public Country( String id, String name ) {
super();
this.CountryId = id;
this.CountryName = name;
}
public void setCountryId(String CountryId) {
this.CountryId = CountryId;
}
public String getCountryId() {
return CountryId;
}
public void setCountryName(String CountryName) {
this.CountryName = CountryName;
}
public String getCountryName() {
return CountryName;
}
}
//----------------------------------
public class Countries {
List<Country> countries = new ArrayList<Country>();
public Countries() {
super();
}
public void setCountries(List<Country> countries) {
this.countries = countries;
}
public List<Country> getCountries() {
if ( countries.size() == 0 ) {
countries.add( new Country("IT","ITALY"));
countries.add( new Country("IN","INDIA"));
countries.add( new Country("US","UNITED STATES"));
}
return countries;
}
public boolean addCountry( Country country ) {
return countries.add( country );
}
// I added this
public boolean removeCountry( Country country ) {
return countries.remove( country );
}
}
//----------------------------------
Then I decided to write (for a start) just plain Java classes and now it looks like below shown code.
It works in IDE, not yet implemented on weblogic server. I hope it would work.
//----------------------------------
package client;
public class Country {
String CountryId;
String CountryName;
public Country() {
super();
}
public Country(String id, String name) {
super();
this.CountryId = id;
this.CountryName = name;
}
public void setCountryId(String CountryId) {
this.CountryId = CountryId;
}
public String getCountryId() {
return CountryId;
}
public void setCountryName(String CountryName) {
this.CountryName = CountryName;
}
public String getCountryName() {
return CountryName;
}
}
//------------------------
package client;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Countries {
List<Country> countries = new ArrayList<Country>();
public Countries() {
super();
}
public void setCountries(List<Country> countries) {
this.countries = countries;
}
public List<Country> getCountries() {
if (countries.size() == 0) {
countries.add(new Country("IT", "ITALY"));
countries.add(new Country("IN", "INDIA"));
countries.add(new Country("US", "UNITED STATES"));
}
return countries;
}
public boolean addCountry(Country country) {
return countries.add(country);
}
// This left unused
public boolean removeCountry(Country country) {
return countries.remove(country);
}
// I added this - what would be more elegant or smarter way to do this?
public void removeCountry(String CountryId, String countryName) {
Iterator<Country> iterator = countries.iterator();
while (iterator.hasNext()) {
Country value = iterator.next();
if (CountryId.equals(value.CountryId) || countryName.equals(value.CountryName)) {
iterator.remove();
break;
}
}
}
}
//------------------
This class is just for test it won't go on server (JDeveloper integrated web logic server)
//-------------------------------
package client;
public class UserInterface {
public static void main(String[] args) {
String CountryId = "";
String CountryName = "";
CountryName = "ENGLAND";
Countries co = new Countries();
co.getCountries();
for (int i = 0; i < co.countries.size(); i++) {
System.out.print(co.getCountries().get(i).getCountryId());
System.out.print(" - ");
System.out.println(co.getCountries().get(i).getCountryName());
}
System.out.println("-------------------------");
// Add some countries
co.countries.add(new Country("DE", "GERMANY"));
co.countries.add(new Country("EN", "ENGLAND"));
for (int i = 0; i < co.countries.size(); i++) {
System.out.print(co.getCountries().get(i).getCountryId());
System.out.print(" - ");
System.out.println(co.getCountries().get(i).getCountryName());
}
System.out.println("-------------------------");
// Remove some countries, this works but can't use this (index)
// co.countries.remove(0); <--- there should be some index instead of 0
// I need to set properties
CountryId = "DE";
CountryName = "";
// Then remove country
co.removeCountry(CountryId, CountryName);
CountryId = "";
CountryName = "ENGLAND";
// Then remove country
co.removeCountry(CountryId, CountryName);
// Is there any way to remove object directly? Parameters should be set by web service iterator.
// co.countries.remove(o);
// co.removeCountry(country)
for (int i = 0; i < co.countries.size(); i++) {
System.out.print(co.getCountries().get(i).getCountryId());
System.out.print(" - ");
System.out.println(co.getCountries().get(i).getCountryName());
}
}
}
//------------------------
I would like to avoid my own iterator as JDeveloper can generate automatically iterators for webservices, but if I can't get it that way, what would be better way to write above mentioned iterator in removeCountry method?
Is there any way to remove object directly with something like this:
co.countries.remove(o);
co.removeCountry(country)
using method
// This left unused
public boolean removeCountry(Country country) {
return countries.remove(country);
}
from class Countries?
Parameters should be set by web service iterator.
I did it this way, and within "Test Web Service" (with manual inputs of course) it works (I can get, add and remove countries, i.e. objects).
I added this into ConutriesProvider application - which provides web service) into Countries class.
Is there any better solution than this?
//---------------------
public boolean removeCountry(Country country) {
Iterator<Country> iterator = countries.iterator();
while (iterator.hasNext()) {
Country value = iterator.next();
if (country.CountryId.equals(value.CountryId) || country.CountryName.equals(value.CountryName)) {
iterator.remove();
return true;
}
}
return false;
}
//--------------------