ArrayList from File (of Abstract class) - java

I have a project I'm working on and pretty much I need to make an ArrayList of the Abstract Class AbstractCustomer; the ArrayList needs to be made up of AbstractCustomer read in from either a data or text file, so I'm trying to do it through a data file but can't wrap my head around it?
This is my class:
import java.io.FileOutputStream;
import java.io.*;
public abstract class AbstractCustomer implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String phoneNumber;
private final int MIN_LENGTH = 10;
public AbstractCustomer() {
}
public AbstractCustomer(String name, String phoneNumber) {
this.name = name;
this.phoneNumber = phoneNumber;
//Phone number is less than 10, but what if it's more than 10?
//Assignment doesn't say, so I will assume it can be more than 10.
//Ya know country codes 1845 and all that
if (phoneNumber.length() < MIN_LENGTH) {
throw new NumberFormatException("Phone number must be at least 10 digits long.");
}
//Parse the long, so this will rule out anything else except digit.
try {
Long.parseLong(phoneNumber);
} catch (NumberFormatException e) {
System.out.println("Phone number can only contain digits, please no dashes ('-') or spaces (' ')");
}
}
abstract double returnDiscountPrice(double itemCost);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractCustomer other = (AbstractCustomer) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (phoneNumber == null) {
if (other.phoneNumber != null)
return false;
} else if (!phoneNumber.equals(other.phoneNumber))
return false;
return true;
}
#Override
public String toString() {
return "AbstractCustomer[name=" + name + ", phoneNumber=" + phoneNumber + "]";
}
}
And this is what I was trying to do:
public void createDataFile() {
try {
FileOutputStream fout = new FileOutputStream("C:\\Users\\Frank\\Desktop\\Assignment\\abstract.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(fout);
outputStream.writeObject(HOW DO I WRITE THE ABSTRACT CLASS TO A FILE HERE????);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Any help is appreciated!

First of all, what you write using IO is an object, not a class. You can read in oracle documentation that:
Abstract classes cannot be instantiated, but they can be subclassed.
What you need to do, is either delete the abstract identifier (i don't see any abstract methods) or inherit this abstract class.
Only then will you be able to instantiate objects of the class (Not)AbstractCustomer and Serialize them using IO.
The only reason that your abstract class is Serializable, is for the classes that extend It, to be serializable as well. I hope this was clear enough.
Here's an example of serialization: (Note that i created a nested class that inherits the abstract class. Don't write It that way. I made it only for demonstration purposes. the main method won't work in that case):
// this cannot be serialized because no objects can be instantiated
public abstract class SerializationTest {
// this can be, because it's not abstract
public static class SerializationTestB extends SerializationTest {
public static void main(String[] args) {
// instantiation of an object that you want to write to file
SomeObject obj = new SomeObject(param, param, param);
SomeObject obj2 = new SomeObject(param, param, param);
SomeObject obj3 = new SomeObject(param, param, param);
SomeObject obj4 = new SomeObject(param, param, param);
SomeObject obj5 = new SomeObject(param, param, param);
SomeObject obj6 = new SomeObject(param, param, param);
// an object that represents the path of the file
File f = new File("some/path/here");
ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
out.writeObject(obj);
out.writeObject(obj2);
out.writeObject(obj3);
out.writeObject(obj4);
out.writeObject(obj5);
out.writeObject(obj6);
out.flush();
out.close();
}
}
}

Related

Why am I getting ClassCastException when trying to deserialize a list

Person class
package model;
import java.io.Serializable;
import java.util.Objects;
public class Person implements Serializable {
private String name;
private String hobby;
private Integer weight;
private Integer age;
public Person(String name, String hobby, Integer weight, Integer age) {
this.name = name;
this.hobby = hobby;
this.weight = weight;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
#Override
public String toString() {
return
"Name = '" + name + "'\n" +
"Hobby = '" + hobby + "'\n" +
"Weight = " + weight + "'\n" +
"Age = " + age + "'\n";
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name) && Objects.equals(hobby, person.hobby) && Objects.equals(weight, person.weight) && Objects.equals(age, person.age);
}
#Override
public int hashCode() {
return Objects.hash(name, hobby, weight, age);
}
}
Serialization
public static void serialization(List<Person> fileList){
if(fileList.size() > 0) {
try (ObjectOutputStream writer = new ObjectOutputStream(new FileOutputStream("dat/serialPersons.ser"))) {
for (int i = 0; i < fileList.size(); i++) {
if (fileList.get(i).getWeight() < 80) {
writer.writeObject(fileList.get(i));
}
}
System.out.println("Serilization complete!");
} catch (FileNotFoundException ex) {
System.err.println(ex);
} catch (IOException ex) {
System.err.println(ex);
}
}
else{
System.out.println("List is empty!!");
}
}
Deserialization
public static void deserialization(){
try(ObjectInputStream objectReader
= new ObjectInputStream(new FileInputStream("dat/serialPersons.ser"))) {
List<Person> deserializedList = (List<Person>)objectReader.readObject();
deserializedList.forEach(System.out::println);
} catch(IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
I am trying to learn some FILE handling in java and been stuck on this for 2 hours, I did multiple examples and the same exception comes out. When I try to deserialize the whole file as a list, I get a class cast exception
Exception in thread "main" java.lang.ClassCastException: class model.Person cannot be cast to class java.util.List (model.Person is in unnamed module of loader 'app'; java.util.List is in module java.base of loader 'bootstrap')
at main.Main.deserialization(Main.java:115)
at main.Main.main(Main.java:32)
If i use fileList.add(objectReader.readObject());
I only get the first one from the file and it is working but I only get the first one.
Any solution would be helpful.
EDIT:
I used a whole list at once with conditioned objects and it all
worked thanks
I think you are writing "Person" object and reading list of "Person" objects. "writer.writeObject(fileList.get(i));" that code would write a single Person because fileList.get(index) returns a single form of Person, however on reading part you are expecting that object to be list of "Person".
For get rid of the issue tiny modification can save the flow, at least it might be worthy to try. But I'm not sure about your entire flow, it is just a suggestion. Hopefully help you :)
writer.writeObject(Arrays.asList(fileList.get(i)));
The exception says you are trying to cast an instance of Person into a List(of Person). That's because readObject returns one Object(in this case one Person). To make a list of Person, split the file by appropriate delimiter(comma, tab, etc), and read one Object at a time. Or you have to define another class that can work like an array of Person(call it PersonList for example), and use readObject to make a PersonList instance. Or writing as ArrayList(or some other List implementations you like) and reading an ArrayList of Person can do the job.
AS it : How do I write multiple objects to the serializable file and read them when the program is used again?
You try to cast a Person to List<Person>.
During Serialization, Write the array list directly.
Serialization
public static void serialization(List<Person> fileList){
if(fileList.size() > 0) {
// First create the list of persons to save
List<Person> personsToSave = new ArrayList<Person>();
try (ObjectOutputStream writer = new ObjectOutputStream(new FileOutputStream("dat/serialPersons.ser"))) {
for (int i = 0; i < fileList.size(); i++) {
if (fileList.get(i).getWeight() < 80) {
personsToSave.add(fileList.get(i));
}
}
// Then save
writer.writeObject(personsToSave);
System.out.println("Serilization complete!");
} catch (FileNotFoundException ex) {
System.err.println(ex);
} catch (IOException ex) {
System.err.println(ex);
}
}
else{
System.out.println("List is empty!!");
}
}

Java reflection with clone

Example I have data layer after
public class DemoData implements Cloneable {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
I want to assign data values (DemoData) to a duplicate data (DemoData clone) layer as follows
public static void main(String[] args) {
DemoData demoData = new DemoData();
demoData.setName("Class Sources");
testReflectionDemo(demoData);
}
private static DemoData testReflectionDemo(DemoData demoData) {
try {
DemoData clone = (DemoData) demoData.clone();
clone.setName(demoData.getName());
clone.setValue(demoData.getValue());
return clone;
} catch (CloneNotSupportedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
I want to convert the method testReflectionDemo(DemoData demoData) to method testReflectionDemo(T t) reflection as shown below.I do not know how to continue, please help me
public <T> T testReflectionDemo(T t){
Class<?> aClass = t.getClass();
for (Method method : aClass.getMethods()) {
}
return null;
}
Thank you all for the help for my question,I've removed the clone method, I just applied reflection.Hi #dabaicai.Your code helped me with the idea,I thought passing the value to the private field would be easier a little.
public static <T> T clazzClone(T t) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> clazzRoot = t.getClass();
Object newInstance = clazzRoot.newInstance();
Field[] fieldsClone = newInstance.getClass().getDeclaredFields();
for (Field fieldClone : fieldsClone) {
fieldClone.setAccessible(true);
fieldClone.set(newInstance, getContent(t, fieldClone.getName()));
}
return (T) newInstance;
}
private static String getContent(Object aClass, String name) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field declaredField = aClass.getClass().getDeclaredField(name);
declaredField.setAccessible(true);
return (String) declaredField.get(aClass);
}
My program means when I need to edit user input data to output the results I want,with a common filter function
fieldClone.set(newInstance,methodYourEdit(getContent(t, fieldClone.getName())));
If the argument of testReflectionDemo is a javabean,it means that the class of argument have several a pair method of setXXX and 'getXXX,and thegetXXXdon't have argument,thesetXXX` just have one argument.If is this,the following code can copy the property from old object to new object.
Class<?> aClass = t.getClass();
Object result = aClass.newInstance();
Map<String,MethodHolder> map=new HashMap<>();
for (Method method : aClass.getMethods()) {
if(method.getName().startsWith("get") && method.getParameterTypes().length==0){
String property=method.getName().substring(3);
MethodHolder hodler = map.get(property);
if(hodler ==null){
map.put(property, new MethodHolder(property, method, null));
continue;
}
hodler.getMethod=method;
}else if (method.getName().startsWith("set") && method.getParameterTypes().length==1) {
String property=method.getName().substring(3);
MethodHolder holder = map.get(property);
if(holder ==null){
map.put(property, new MethodHolder(property, null, method));
continue;
}
holder.setMethod=method;
}
}
List<MethodHolder> collect = map.values().stream().filter(item -> item.setMethod != null && item.getMethod != null).collect(Collectors.toList());
for (MethodHolder holder : collect) {
Object property = holder.getMethod.invoke(t);
holder.setMethod.invoke(result,property);
}
return (T)result;
The MethodHolder just have some field:
public static class MethodHolder{
private String property;
private Method getMethod;
private Method setMethod;
public MethodHolder() {
}
public MethodHolder(String property, Method getMethod, Method setMethod) {
this.property = property;
this.getMethod = getMethod;
this.setMethod = setMethod;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MethodHolder)) return false;
MethodHolder that = (MethodHolder) o;
return Objects.equals(property, that.property);
}
#Override
public int hashCode() {
return Objects.hash(property);
}
}
Pay attention of that the following code just make shallow copy.

A combination of methods in a list

In a Gate class I have method public List<Signal> inspect(List<Signal> inputs) which should contain a combination of feed(), propagate(), and read(). That's the only method I have left to finish but getting an error. Could smb please help me with this method? NOTE: propagate() is left abstract to be overriden by childclasses of Gate class. The method public List<Signal> inspect(List<Signal> inputs) should combine feed(), propagate(), and read().
import java.util.*;
public abstract class Gate implements Logic {
private List<Wire> inputs;
private Wire output;
private String name;
public Gate(String name, List<Wire> ins, Wire out)
{
this.name = name;
this.output = out;
if(ins.size() == 0 || ins.isEmpty())
throw new ExceptionLogicParameters(false, 1, 0);
else
this.inputs = ins;
}
#Override
public void feed(List<Signal> inSigs)
{
if(inSigs.size() != inputs.size())
throw new ExceptionLogicParameters(false, inputs.size(), inSigs.size());
else
{
for(int i = 0; i < inSigs.size(); i++)
{
inputs.get(i).setSignal(inSigs.get(i));
}
}
}
#Override
public void feed(String name)
{
if(!(this.name.equals(name)))
throw new ExceptionLogicMalformedSignal(name.charAt(0), "Invalid logic input");
else
{
Signal signalValue = Signal.fromString(name.charAt(0));
}
}
#Override
public List<Signal> read()
{
List<Signal> signals = new ArrayList<>();
signals.add(output.getSignal());
return signals;
}
#Override
public abstract boolean propagate();
#Override
public List<Signal> inspect(List<Signal> inputs)
{
List<Signal> allMethods = new ArrayList<>();
allMethods.add(this.feed(inputs));
allMethods.add(this.propagate());
allMethods.add(this.read());
}
#Override
public String toString()
{
return this.name+"( " + inputs.toString() + " | " + output.toString() + " )";
}
#Override
public boolean equals(Object other)
{
if(other instanceof Gate)
{
Gate someGate = (Gate)other;
return (this.inputs == someGate.inputs) && (this.output.equals(someGate.output)
&& (this.name.equals(someGate.name)));
}
else
return false;
}
}
All your methods have no return type.
When you do this
allMethods.add(this.feed(inputs));
allMethods.add(this.propagate());
allMethods.add(this.read());
It would not return anything and hence nothing is added to the list which will throw error.
Your list of of type signal
List<Signal> allMethods = new ArrayList<>();
You need to change the return type of all methods to Signal to add them to the list. Like you cant add an Integer to a List<String> you cannot add anything else than type Signal to the List<Signal>
I'm no sure of your code logic and if you can change the return type or not, but changing all methods return type to Signal should work fine.
Also, you need a return statement for
public List<Signal> inspect(List<Signal> inputs)
You have to always return something if method is not void and the return type should be same as function type

How to serialize complex Json object to QueryString for HTTP Get using Jackson?

Say that I have following objects:
public class ComplexJacksonObject extends BaseJsonObject {
public int Start;
public int Count;
public Person MyPerson;
public class Person extends BaseJsonObject {
public String Firstname;
public String Lastname;
public Address Where;
}
public class Address extends BaseJsonObject {
public String Street;
public int Number;
}
}
Obviously when I request JSON of this with Jackson I get something like:
public String toJson(ComplexJacksonObject obj) {
try {
return generateMapper().writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// returned: {"MyPerson":{"Firstname":"First","Lastname":"Last","Where":{"Street":"Street","Number":15}},"Count":1,"Start":2}
However what I need for QueryString is that top property pairs are converted to Key=Value& format, so something like:
MyPerson={"Firstname":"First","Lastname":"Last","Where":{"Street":"Street","Number":15}}&Count=1&Start=2
Plus of course MyPerson=[This_Part_Needs_To_Be_Url_Encoded].
Is there any generic method in Jackson that would do this for me automatically? Or will I be forced to come up with something my own? Some String replacement regex? Any ideas?
[Edit] NOTE: I misunderstood the question. My answer below answers how to parse the JSON and get a Java object. You wanted to get the Key value pairs where JSON is the value for the object. The below answer will not answer that question. Sorry for the confusion.
You can fix this issue by using Jackson annotations to the java model and adding a "type" to the JSON object. You might want to research it for your purposes but here is an example from some code I have done in the past.
public class Requirement {
private String title;
private String reqId;
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
#JsonSubTypes({
#JsonSubTypes.Type(value=CountRequirementList.class, name="COUNT"),
#JsonSubTypes.Type(value=AndRequirementList.class, name="AND"),
#JsonSubTypes.Type(value=OrRequirementList.class, name="OR")
})
private List<RequirementList> fulfillments;
where the baseObject is the RequirementList and the class names are types of the requirements list.
To make things easier going back and forth from JSON, it is sometimes convenient to just add a type to the object. I have included more code below in case it helps. (note: I did not include all the getters and setters that are needed for Jackson)
public abstract class RequirementList {
private LogicType type;
private String id;
private String title;
private String description;
protected float requiredCount; //For count type subclass. Designed to be count of credits
private List<Object> fulfillments;
}
public class OrRequirementList extends RequirementList {
public OrRequirementList() {
super();
super.setType(LogicType.OR);
}
}
See my answer for this question How to serialize ANY Object into a URI?. You have to add only URL encoding to my solution. For example you can use UrlEncoder#encode(String s, String enc) method.
OK,
Here is the holder object:
public class ComplexJacksonObject extends BaseJsonObject {
public int Start;
public int Count;
public Person MyPerson;
public List<String> Strings;
public class Person extends BaseJsonObject {
public String Firstname;
public String Lastname;
public Address Where;
}
public class Address extends BaseJsonObject {
public String Street;
public int Number;
}
}
Here is how I initialize it:
ComplexJacksonObject cjo = new ComplexJacksonObject();
cjo.Count = 1;
cjo.Start = 2;
cjo.Strings = new ArrayList<String>();
cjo.Strings.add("One");
cjo.Strings.add("Two");
cjo.MyPerson = cjo.new Person();
cjo.MyPerson.Firstname = "Fi\",=[]{}rst";
cjo.MyPerson.Lastname = "Last";
cjo.MyPerson.Where = cjo.new Address();
cjo.MyPerson.Where.Street = "Street";
cjo.MyPerson.Where.Number = 15;
String result = cjo.toQueryString();
// Strings=%5B%22One%22%2C%22Two%22%5D&MyPerson=%7BFirstname%3A"Fi%5C%5C%22%2C%3D%5B%5D%7B%7Drst"%2CLastname%3A%22Last%22%2CWhere%3A%7BStreet%3A%22Street%22%2CNumber%3A15%7D%7D&Start=2&Count=1
And finally the method that makes this happen:
public String toQueryString() {
StringBuilder sb = new StringBuilder();
for (Field field : this.getClass().getDeclaredFields()) {
if (sb.length() > 0) {
sb.append("&");
}
Class cls = field.getType().getSuperclass();
// serializing my complex objects - they all inherit from BaseJsonObject class
if (cls != null && cls.equals(BaseJsonObject.class)) {
BaseJsonObject bjo = (BaseJsonObject) getFieldValue(field);
String str = toJson(bjo, true);
sb.append(field.getName()).append("=").append(Uri.encode(str));
}
// serializing lists, they are all List<T>
else if (field.getType().equals(List.class)) {
List bjo = (List) getFieldValue(field);
String val = toJson(bjo, false);
sb.append(field.getName()).append("=").append(Uri.encode(val));
}
// serializing simple fields
else {
Object bjo = getFieldValue(field);
String val = toJson(bjo, false).replaceAll("^\"|\"$", "");
sb.append(field.getName()).append("=").append(Uri.encode(val));
}
}
return sb.toString();
}
private Object getFieldValue(Field field) {
try {
return field.get(this);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
private static ObjectMapper generateMapper() {
ObjectMapper om = new ObjectMapper();
// om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
om.setDateFormat(new JacksonSimpleDateFormat());
return om;
}
public String toJson() {
try {
return generateMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
public String toJson(Object o, boolean noQuoteProperties) {
try {
ObjectMapper om = generateMapper();
if (noQuoteProperties) {
om.configure(com.fasterxml.jackson.core.JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
om.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
}
return om.writeValueAsString(o);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}

Populating HashMaps of HashMap of Objects

I have code I am working on, but I am having issues populating the HashMap of HashMaps. The declaration goes as thus;
HashMap<Games, HashMap<Store, Integer>> myMap = new HashMap<Games, HashMap<Store, Integer>>();
Where Game and Store are separate object classes, with only a class variable title.
How do I create instances of the objects in the HashMaps and also populate the two hashmaps. Because I need to tag an Integer to the game in a particular store. Whereas there are different stores and different games in each store.
Thanks in Advance
Edit
Games Class
package gameStore;
public class Games {
private String title;
public Games(String inTitle){
setTitle(inTitle);
}
private String getTitle() {
return title;
}
private void setTitle(String title) {
this.title = title;
}
}
Stores Class
package gameStore;
public class LocalStores {
private String nameOfStore;
public LocalStores(String inNameOfStore){
setNameOfStore(inNameOfStore);
}
private void setNameOfStore(String nameOfStore){
this.nameOfStore = nameOfStore;
}
}
I would do something like this:
void addToMap(Games games, Store store, int value) {
HashMap<Store,Integer> m = myMap.get(games);
if (m == null) {
m = new HashMap<Store,Integer>();
myMap.put(games, m);
}
m.put(store, value);
}
UPDATE:
Since Games and Store are both used as keys to a HashMap, I would recommand that you add the hashCode and equals methods:
Games:
public int hashCode() {
return title.hashCode();
}
public boolean equals(Object obj) {
if (!(obj instanceof Games)) {
return false;
}
Games other = (Games)obj;
return title.equals(other.title);
}
LocalStores:
public int hashCode() {
return nameOfStore.hashCode();
}
public boolean equals(Object obj) {
if (!(obj instanceof LocalStores)) {
return false;
}
LocalStores other = (LocalStores)obj;
return nameOfStore.equals(other.nameOfStore);
}
Now, to keep it simple, let's say that each line of your input file contains three fields separated by tabs: the games' title, the store's name, and the integer value. You would read it as follows:
InputStream stream = new FileInputStream("myfile");
try {
Reader reader = new InputStreamReader(stream, "UTF-8"); // or another encoding
try {
BufferedInputStream in = new BufferedInputStream(reader);
try {
String line = in.readLine();
while (line != null) {
String[] fields = line.split("[\\t]");
if (fields.length == 3) {
addToMap(new Games(fields[0]), new LocalStores(fields[1]), Integer.parseInt(fields[2]));
}
line = in.readLine();
}
} finally {
in.close();
}
} finally {
reader.close();
}
} finally {
stream.close();
}

Categories

Resources