I recently started working with JSON in Java. We have been setting and getting our values as follows from this JSONArray:
[{"productId":"1"},{"productName":"hammer"}]
JSONObject jo = ja.getJSONObject(0);
We could easily get the values by calling jo.getString("productId"); which would return the 1.
The problem is that sometimes we get different types of JSON objects. They look like this:
[{"name":"productId", "value":"1"},{"name":"productName", "value":"hammer"}]
Is there a way to easily eliminate those predicate name/value and just group the actual name and value together (as in the first example)?
The short answer is no.
The longer answer is that you're not working with JSON, you're working with someone's misunderstanding of JSON.
Both of your examples look a bit like JSON, but they're both bogus.
[] is an array.
{} is an object.
Your first string [{"productId":"1"},{"productName":"hammer"}]
is an array of two objects, where each object has one property.
It's confusing to put dissimilar objects into an array together, but that's going on in both of your examples.
The second example [{"name":"productId", "value":"1"},{"name":"productName", "value":"hammer"}] shows an array of two objects, but again, the objects are dissimilar.
I think what they're going for is more like [{"productId":"1","productName":"hammer"}], so I guess the long answer to your question is that you need to go to whomever is providing this "JSON" and tell them to fix it.
To give you a clearer idea of the correspondence between objects (in Java and otherwise) and JSON, check out the Java program below:
public class Product {
String productName;
String productId;
public Product(String productId,String productName){
this.productName = productName;
this.productId = productId;
}
public String toString(){return toJSONString();}
public String toJSONString(){
return "{\"productId\":\""+productId+",\"productName:\""+productName+"\"}";
}
public static String arrayToJSONString(Product[] arry){
StringBuilder sb = new StringBuilder();
sb.append("["+arry[0]);
for (int n =1;n<arry.length;n++){
sb.append(","+arry[n]);
}
sb.append("]");
return sb.toString();
}
public static void main(String [] args){
Product p1 = new Product("1","hammer");
Product[] arry = {p1};
Product[] arry2 ={p1,new Product("2","shovel"), new Product("3","manure")};
System.out.println("One object");
System.out.println(" "+p1);
System.out.println("An array containing one object");
System.out.println(" "+Product.arrayToJSONString(arry));
System.out.println("An array containing three objects");
System.out.println(" "+Product.arrayToJSONString(arry2));
}
}
Here's the output showing the proper JSON representation:
One object
{"productId":"1,"productName:"hammer"}
An array containing one object
[{"productId":"1,"productName:"hammer"}]
An array containing three objects
[{"productId":"1,"productName:"hammer"},{"productId":"2,"productName:"shovel"},{"productId":"3,"productName:"manure"}]
(Newlines are an artifact of the HTML, not JSON)
Related
I need to transform an array of one type to an array of another type.
More specifically, I need to pull just a couple fields from each object in the starting array to create the resulting array, which will contain only those 2 fields, though named differently.
For example, let's say I have an array of Thing objects:
public class Thing {
private String id;
private String description;
... // other fields
}
I need to create from that an array of Item objects:
public class Item {
private String code;
private String data;
...
}
... where the id from each Thing becomes code in each Item; and description becomes data.
I've seen examples of using the Stream api to transform an array of objects to an array of Strings. But it's unclear to me thus far how to transform an object to another object.
Try this.
record Thing(String id, String description) {}
record Item(String coded, String data) {}
public static void main(String[] args) {
Thing[] array = {new Thing("1", "one"), new Thing("2", "two")};
Item[] transformed = Arrays.stream(array)
.map(thing -> new Item(thing.id(), thing.description()))
.toArray(Item[]::new);
System.out.println(Arrays.toString(transformed));
}
output:
[Item[coded=1, data=one], Item[coded=2, data=two]]
Im working on a project in which it reads book titles in from a .txt file and puts them into an arraylist, then the arraylist is converted to an array, The user enters a number which is the books reference number, then it does a linear search to find that book. Im just having a little trouble with the code for my linear search, heres what I have:
private void FindItActionPerformed(java.awt.event.ActionEvent evt) {
String input;
int int1;
input = Input.getText();
int1 = Integer.parseInt(input);
for(int i=0; i<bookList.length; i++){
if (bookList[i].equals(int1)){
Output1.setText("The Book is " + bookList[i]);
}
}
}
It says .equals() on incompatible types, not sure how to fix, thanks for any help you may be able to offer
public class Book{
String referenceNumber, title;
public Book(String _referenceNumber, String _title){
referenceNumber = _referenceNumber;
title = _title;
}
}
ArrayList <Book> Books = new ArrayList <Book> ();
Book [] bookList;
Here is the Book class and arrays
bookList[i].referenceNumber.equals(input)
This should solve your compatibility problem
On side note, not a good idea to make your class members public..try using getters and setters to change the members..
A little more details: as GoldRoger correctly pointed out, you need to access the referenceNumber of your book. It says incompatible types because you are comparing apples against carrots, or better said Numbers against Books. Accessing the referenceNumber of the book through:
bookList[i].referenceNumber.equals(input)
is the correct approach.
I have been looking for this answer for the past 2 hours with no luck so I am posting my question now. I am sorry if it is similar to one already asked but I could not get an clear answer from what was already answered.
Here goes:
I am doing a school project in which we read in a text file of "Computer" objects (there are 8 fields in total in this form: manufacturer:model:memory:diskMemory:CPU:opticalDrive:OSVersion:RetailPrice)
once the file is read you are supposed to separate the fields and construct an array of Computer objects using the constructor that accepts all the above parameters (separated) and store their reference in each position of the array.
Here is where my question is:
Afterwards you display a menu and the user selects a few option, the simplest is just to display the list of computer object in the following form on a JOPtionPane:
Manufacturer1 model1 memory1 disk1 CPU1 optical1 OS1 retailPrice1
Manufacturer2 model2 memory2 disk2 CPU2 optical2 OS2 retailPrice2
and so on until you finish the array. I cannot figure out how to condense the array of objects into a single string that is in the form above. The Computer class has a getMethod for each of those fields I am just having trouble getting them to be aligned in that way. It has to be on a JOPtionPane the simple .INFORMATION_MESSAGE kind. If you all need to see the Computer class code let me know and I can post it. This is my first time posting on this website so I apologize if it is in improper form.
Thank you so much for your help,
Bob
EDIT:
This is what the output should look like:
http://i229.photobucket.com/albums/ee38/Yukijin-Uchiha/ScreenShot2014-03-14at113759AM_zps05b5dbb5.png
If you implement the Computer classes toString() function properly, you should get just what you want. For example:
public class Computer {
public String toString() {
return (new StringBuilder(getManufacturer()).append(":").
append(getModel()).append(":").
append(getMemory()).append(":").
append(getDiskMemory()).append(":").
append(getCPU()).append(":").
append(getOpticalDrive()).append(":").
append(getOSVersion()).append(":").
append(getRetailPrice()).
toString();
}
}
Then call it with
String computerObjAsString = aComputerInstance.toString();
To do this with an array of Computer objects:
StringBuilder outBldr = new StringBuilder();
for(Computer cmptr : anArrayOfComputers) {
outBldr.append(cmptr).append(System.getPropetry("line.separator", "\r\n"));
}
System.out.println(outBldr);
You should look to override the toString() method in your Computer objects to include the details that you want printed. Then, for each Computer object in your array, call <object name>.toString() to display the appropriate information.
public class Computer {
private String name;
private String type;
public Computer(String n, String t) {
this.name = n;
this.type = t;
}
public String toString() {
return "Name: " + this.name + " Type: " + this.type;
}
public static void main(String[] args) {
Computer c = new Computer("atari","old-school");
System.out.println(c.toString());
}
}
I have json string represenatation of some object
class objects is
public class SMPBBaseObjectsList {
public ArrayList<Object> data = new ArrayList<>();
public Integer count;
public Integer limitFrom;
public Integer limitTo;
public Boolean hasMore;
public String dataItemsClass;
}
And i have json
{"classItem":"smpb.utility.classes.SMPBBaseObjectsList","dataItemsClass":"smpb.base.classes.SMPBUser","dataSliceCode":"012013","data":[{"id":1374046117510970000,"Name":"Test3","classItem":"smpb.base.classes.SMPBUser","dataSliceCode":"012013"}],"filter":{"orderItems":[],"filterItems":[]}}
I try parse this json and create object of my class with next code:
String json = "{\"classItem\":\"smpb.utility.classes.SMPBBaseObjectsList\",\"dataItemsClass\":\"smpb.base.classes.SMPBUser\",\"dataSliceCode\":\"012013\",\"data\":[{\"id\":1374046117510970000,\"Name\":\"Test3\",\"classItem\":\"smpb.base.classes.SMPBUser\",\"dataSliceCode\":\"012013\"}],\"filter\":{\"orderItems\":[],\"filterItems\":[]}}";
SMPBBaseObjectsList list = new GsonBuilder().create().fromJson(json, SMPBBaseObjectsList.class);
System.out.println("BEFORE:" + json);
System.out.println("AFTER: " + list);
System outputs:
BEFORE:{"classItem":"smpb.utility.classes.SMPBBaseObjectsList","dataItemsClass":"smpb.base.classes.SMPBUser","dataSliceCode":"012013","data":[{"id":1374044905885298000,"Name":"Test3","classItem":"smpb.base.classes.SMPBUser","dataSliceCode":"012013"}],"filter":{"orderItems":[],"filterItems":[]}}
AFTER: {"classItem":"smpb.utility.classes.SMPBBaseObjectsList","dataItemsClass":"smpb.base.classes.SMPBUser","dataSliceCode":"012013","data":[{"Name":"Test3","id":1.374044905885298011E18,"classItem":"smpb.base.classes.SMPBUser","dataSliceCode":"012013"}],"filter":{"orderItems":[],"filterItems":[]}}
As u can see in Json String i have ID with value 1374044905885298000 , but when object serialized from string i got 1.374044905885298011E18
And problem is what this representation of Long lost last zeros 0000 and i got Long 1374044905885297920
Why? and how fix it?
Data in Array is String map, and it's already all Long id Double format.
I try registerAdapater for Long or Double but never triggered.
Version of Gson 2.2.4
UPDATE
It's not duplicate of question
How to prevent Gson from converting a long number (a json string ) to scientific notation format?
I can't tell exactly what the problem is, but you can solve it by creating another class, i.e. Data to use in the List instead of the Object class... I tried this code and it's working fine for me!
So, you need to replace the ArrayList<Object> in your SMPBBaseObjectsList by:
public ArrayList<Data> data = new ArrayList<>()
And create a new class like this:
public class Data {
public Long id;
public String Name;
public String classItem;
public String dataSliceCode;
}
I guess there's an issue when parsing the JSON to an Object object, it probably makes some conversion that leads to that number formatting, but unfortunately I'm not an expert in this Java low-level issues...
Anyway, with this code you are explicitly specifying that you want that value parsed into a Long, so there's no problem!
This is more of a design question with implications for code simplicity vs. performance.
Lets say you want to make sure a set of values for a given user id are the same between two systems. The example here is to check that a student id has the same number of course enrollments in System A and System B.
For this we create:
List<String> studentList = new ArrayList<String>();
Set<String> sysAEnrollments = new HashSet<String>();
Set<String> sysBEnrollments = new HashSet<String>();
private Map<String, String> badEnrollList = new HashMap<String, String>();
And fill them appropriately, given a list of student ids(studentList):
studentList = getCurrentStudentList();
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.containsAll(sysBEnrollments)){
badEnrollList.put(id, getBadEnrollmentsById(id, sysAEnrollments, sysBEnrollments));
}
}
Question: What should the method 'getBadEnrollmentsById' return?
Either a concatenated string with enough meaning so it can just be printed out.
Or have a new object, for example another collection with the list of course ids that could be used for further processing but harder to use for printed output.
Is it worth designing thoroughly all expected objects or replace some of them with concatenated strings for clarity and performance?
NOTES:
System A is preferred as the authoritative source
Output from getBadEnrollmentsById should have all courses and flag those missing in system B.
PROPOSED SOLUTION: (2012-SEP-14)
EDIT (2012-SEP-17): Updated the Course class to include hashCode and equals
As suggested by user351721 I continued modelling the remaining objects that match the expected results/requirements.
Slight changes made a big difference and allowed me to go over this design flaw and finish with the implementation.
The revised collections are:
List<String> studentList = new ArrayList<String>();
Enrollment sysAEnrollments;
Enrollment sysBEnrollments;
Map<String, List<String>> badEnrollList = new HashMap<String, List<String>>();
And we populate the Enrollments:
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.getCourses().containsAll(sysBEnrollments.getCourses())){
List<String> missingCourses = getProblemEnrollmentListById(id, sysAEnrollments, sysBEnrollments);
badEnrollList.put(id, missingCourses);
}
}
So for now the output can be printed from badEnrollList by getting at each ArrayList and printing the course names. A course name with a * will mean that it's missing in sysB.
The Enrollment class looks like this:
public class Enrollment {
private Set<Course> courses = new HashSet<Course>();
public void setCourses(Set<Course> courses){
this.courses = courses;
}
public Set<Course> getCourses(){
return this.courses;
}
}
And the Course class ended up like this:
public class Course {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
// Must override hashCode() and equals()
#Override
public boolean equals(Object o){
if (o == this)
return true;
if (!(o instanceof Course))
return false;
Course c = (Course) o;
return c.id.equals(this.id) && c.name.equals(this.name);
}
#Override
public int hashCode(){
// Magic numbers as shown on Joshua Bloch's book "Effective Java" 2nd Edition, p.48
int result = 17;
result = 31 * this.id.hashCode();
result = 31 * this.name.hashCode();
return result;
}
}
The changes might look subtle but the important clue is that Enrollments are not a collection of strings, Enrollments are a collection of Courses AND each Course has a name and a availability property. They don't seem to do much but by using them I am defining the objects that I'm working with and documenting how these classes can be reused in the future.
"Growing Object-Oriented Software, Guided by Tests" addresses this question: chapter 7, "Value Types". Worth reading. An excerpt:
The more code we write, the more we’re convinced that we should define types to represent value concepts in the domain, even if they don’t do much. It helps to create a consistent domain model that is more self-explanatory. If we create, for example, an Item type in a system, instead of just using String, we can f ind all the code that’s relevant for a change without having to chase through the method calls
concatenated strings
would mean you have to define a pattern and corresponding set of valid strings and implement validation and translation to entity classes. Providing an interface or class would make it easier to update your code in a year or so, not to mention other programmers that might work with your application. Why not store student, enrollment or course objects in badEnrollList? How do these objects look like and what do you want to do with them?
In general: Yes, designing thoroughly all expected objects is worth it.
I feel that a collection, such as List<String> would be a desirable return value. This allows you to more efficiently capture multiple discrepancies between the two sets, and process the missing courses in your second object more intuitively. Printing the list wouldn't be that hard, either - depending on how you wished to convey the information.
It's also worth mentioning that the .equals() method for Set is a cleaner and more intuitive way to ensure equivalence between two sets.
Instead of using all these sets and maps, I'd use Plain Old Java Objects (POJOs) that reflect the actual business objects in question. From what you've indicated, you have Students who have an id of some sort, and who are enrolled in classes on System A and on System B. I would build up a set of Student objects defined like so:
public class Student {
private String id;
private List<String> enrollmentsA;
private List<String> enrollmentsB;
// appropriate getters and setters
}
Depending on if you want to do anything else with Classes, it may even be preferable to create some form of EnrolledClass object to represent that too.
Within the students class, I'd then have a method that would determine the "bad" enrollments. If all that you want to do with this data is generate an email message, it may even be as simple as a String:
public String getBadEnrollmentsMessage() {
List<String> enrolledBoth = getCommonEnrollments();
List<String> enrolledOnlyA = getAOnlyEnrollments();
List<String> enrolledOnlyB = getBOnlyEnrollments();
StringBuilder output;
// format the contents of the above lists into output
// format should be however you want it in the email.
return output.toString();
}
Then you could have a map of Students to email enrollments messages:
HashMap<Student, String> studentEmails;
for (Student s : allStudents) {
studentEmails.put(s, s.getBadEnrollmentsMessage());
}
Of course, if you have a method like getBadEnrollmentsMessage(), I'm not even sure you need the Map of students and strings in the first place. Frankly you could just create a sendEnrollmentEmail method, pass in a Student, and extract the message via getBadEnrollmentsMessage() right there.