Avatar billede it-dyret Nybegynder
18. oktober 2007 - 11:18 Der er 14 kommentarer og
1 løsning

XStream: serialisering af objekter, der arver fra GUI

Jeg har fået til opgave at lave persistens af et større system via XStream. Undervejs i dette arbejde har der vist sig en problemstilling, som jeg gerne vil have hjælp til.

Lettere forenklet har jeg bl.a. disse klasser:

- class Unit (modelklasse, som holder oplysninger om en enhed + reference til UnitPosition + reference til ModelContainer)
- class UnitPosition (GUI klasse, som arver fra java.awt.image.BufferedImage)
- class ModelContainer (modelklasse, som fungerer som container for en lang række andre modelklasser)

Det er simpelt at serialisere "Unit", men problemet opstår ved, at jeg har brug for at serialisere x- og y-koordinaterne fra "UnitPosition" - og det er ikke holdbart blot at pudse XStream på den klasse også, da den gemmer en masse overflødig information fra java.awt.image.BufferedImage.

Hvordan får jeg serialiseret alle oplysninger fra "Unit" og kun nogle udvalgte oplysninger fra "UnitPosition"?

Jeg har forsøgt at lave en Converter til "UnitPosition", hvilket næsten får mig i mål... Lige indtil jeg skal gemme referencen til "ModelContainer". Hvordan serialiserer og de-serialiserer man en reference til en anden klasse via en Converter??

Hvis der er en nemmere måde at komme i mål på end at lave en Converter, er jeg naturligvis åben for andre løsninger!
Avatar billede arne_v Ekspert
19. oktober 2007 - 01:49 #1
Jeg tror at problemet skyldes dit design.

Du blander model klasser og view klasser.

UnitPosition indeholder både data og GUI.

Split den op i UnitPosition med ren data og UnitPositionComponent med ren GUI, Unit
kan så nemt serialiseres og UnitPositionComponent har en constructor/setter/noget
som tager en UnitPosition som argument.
Avatar billede it-dyret Nybegynder
19. oktober 2007 - 10:19 #2
Jeg er helt enig med dig i, at designet ikke er optimalt - og (heldigvis) er det ikke mit design :)

Jeg vil også gerne ændre det, men det vil desværre blive en ret stor opgave, da det er et omfattende projekt. Så inden jeg tager en dyb indånding og meddeler kunden, at designet skal laves om, bliver jeg nødt til at spørge, om der ikke er en genvej til målet?
Avatar billede arne_v Ekspert
20. oktober 2007 - 04:56 #3
Der er faktisk et eksempel på hvordan du styrer noget sådan på XStream hjemme siden.

Men her er et eksempel jeg har strikket sammen:

package october;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class CustomSerialize {
    public static void main(String[] args) throws IOException {
        Big o = new Big("Fine", new Problem("OK", "Ooops"));
        // standard
        XStream xs1 = new XStream(new DomDriver());
        xs1.alias("big", Big.class);
        OutputStream s1 = new FileOutputStream("C:\\standard.xml");
        xs1.toXML(o, s1);
        s1.close();
        XStream xs2 = new XStream(new DomDriver());
        xs2.alias("big", Big.class);
        InputStream s2 = new FileInputStream("C:\\standard.xml");
        Big o2 = (Big)xs2.fromXML(s2);
        s2.close();
        System.out.println(o2);
        // special
        XStream xs3 = new XStream(new DomDriver());
        xs3.alias("big", Big.class);
        xs3.registerConverter(new ProblemConverter());
        OutputStream s3 = new FileOutputStream("C:\\special.xml");
        xs3.toXML(o, s3);
        s3.close();
        XStream xs4 = new XStream(new DomDriver());
        xs4.alias("big", Big.class);
        xs4.registerConverter(new ProblemConverter());
        InputStream s4 = new FileInputStream("C:\\special.xml");
        Big o4 = (Big)xs4.fromXML(s4);
        s4.close();
        System.out.println(o4);
    }
}

class Big {
    private String good;
    private Problem mixed;
    public Big() {
        this("", new Problem());
    }
    public Big(String good, Problem mixed) {
        super();
        this.good = good;
        this.mixed = mixed;
    }
    public String getGood() {
        return good;
    }
    public void setGood(String good) {
        this.good = good;
    }
    public Problem getMixed() {
        return mixed;
    }
    public void setMixed(Problem mixed) {
        this.mixed = mixed;
    }
    public String toString() {
        return "[" + good + "," + mixed + "]";
    }
}

class Problem {
    private String good;
    private String bad;
    public Problem() {
        this("", "");
    }
    public Problem(String good, String bad) {
        super();
        this.good = good;
        this.bad = bad;
    }
    public String getGood() {
        return good;
    }
    public void setGood(String good) {
        this.good = good;
    }
    public String getBad() {
        return bad;
    }
    public void setBad(String bad) {
        this.bad = bad;
    }
    public String toString() {
        return "[" + good + "," + bad + "]";
    }
}

class ProblemConverter implements Converter {
    public void marshal(Object o, HierarchicalStreamWriter hsr, MarshallingContext mc) {
        Problem o2 = (Problem)o;
        hsr.startNode("good");
        hsr.setValue(o2.getGood());
        hsr.endNode();
    }
    public Object unmarshal(HierarchicalStreamReader hsr, UnmarshallingContext mc) {
        while (hsr.hasMoreChildren()) {
            hsr.moveDown();
            if(hsr.getNodeName().equals("good")) {
                Problem o = new Problem();
                o.setGood(hsr.getValue());
                return o;
            }
            hsr.moveUp();
        }
        return null;
    }
    public boolean canConvert(Class c) {
        return c.equals(Problem.class);
    }
}
Avatar billede arne_v Ekspert
20. oktober 2007 - 04:57 #4
Big = Unit
Problem = UnitPosition
Problem good = x og y
Problem bad = GUI
Avatar billede arne_v Ekspert
20. oktober 2007 - 04:58 #5
Hmm.

Følgende er nok nemmere at udvide til flere felter:

    public Object unmarshal(HierarchicalStreamReader hsr, UnmarshallingContext mc) {
        Problem o = new Problem();
        while (hsr.hasMoreChildren()) {
            hsr.moveDown();
            if(hsr.getNodeName().equals("good")) {
                o.setGood(hsr.getValue());
            }
            hsr.moveUp();
        }
        return o;
    }
Avatar billede arne_v Ekspert
18. november 2007 - 22:58 #6
OK ?
Avatar billede it-dyret Nybegynder
19. november 2007 - 10:37 #7
Med mit kendskab til dine svar er det sandsynligvis mere end OK :) Men jeg har ikke nået til det endnu grundet pludselig omprioritering af opgaver, men jeg skal nok nå det inden for den kommende uge... Jeg vender tilbage - og beklager responstiden.
Avatar billede it-dyret Nybegynder
03. december 2007 - 17:21 #8
Tak for det meget udførlige eksempel, som kan bringes til at løse mit problem :) Men hvad nu hvis mit problem havde været en tand større - og jeg havde haft en reference til en modelklasse i min UnitPosition (svarende til hvis din Problem klasse)?

Altså hvordan serialiserer og de-serialiserer man en reference til en anden klasse via en Converter??
Avatar billede arne_v Ekspert
03. december 2007 - 17:52 #9
Uanset hvor mange niveauer der er saa kan modellen udvides til at klare det.

canConvert skal skal returnere true for de klasser der kraever speciel behandiling

unmarshal skal bruge modellen i 20/10-2007 04:58:24

marshal skal udvides til ogsaa at kunne haandtere forskelleige klasser
Avatar billede arne_v Ekspert
04. december 2007 - 03:22 #10
Det begynder jo at blive lidt langhåret.

Men prøv og studer de genererede XML filer og output fra dette program:

package december;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class CustomSerialize {
    public static void main(String[] args) throws IOException {
        C1 o = new C1();
        System.out.println(o);
        // standard
        XStream xs1 = new XStream(new DomDriver());
        xs1.alias("C1", C1.class);
        xs1.alias("C2", C2.class);
        xs1.alias("C3", C3.class);
        OutputStream s1 = new FileOutputStream("C:\\standard.xml");
        xs1.toXML(o, s1);
        s1.close();
        XStream xs2 = new XStream(new DomDriver());
        xs2.alias("C1", C1.class);
        xs2.alias("C2", C2.class);
        xs2.alias("C3", C3.class);
        InputStream s2 = new FileInputStream("C:\\standard.xml");
        C1 o2 = (C1)xs2.fromXML(s2);
        s2.close();
        System.out.println(o2);
        // let us wait
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        // special
        XStream xs3 = new XStream(new DomDriver());
        xs3.alias("C1", C1.class);
        xs3.alias("C2", C2.class);
        xs3.alias("C3", C3.class);
        xs3.registerConverter(new ProblemConverter());
        OutputStream s3 = new FileOutputStream("C:\\special.xml");
        xs3.toXML(o, s3);
        s3.close();
        XStream xs4 = new XStream(new DomDriver());
        xs4.alias("C1", C1.class);
        xs4.alias("C2", C2.class);
        xs4.alias("C3", C3.class);
        xs4.registerConverter(new ProblemConverter());
        InputStream s4 = new FileInputStream("C:\\special.xml");
        C1 o4 = (C1)xs4.fromXML(s4);
        s4.close();
        System.out.println(o4);
    }
}

class ProblemConverter implements Converter {
    public void marshal(Object o, HierarchicalStreamWriter hsr, MarshallingContext mc) {
        if(o instanceof C1) {
            hsr.startNode("s");
            mc.convertAnother(((C1)o).getS());
            hsr.endNode();
            hsr.startNode("o2");
            mc.convertAnother(((C1)o).getO2());
            hsr.endNode();
        } else  if(o instanceof C2) {
            hsr.startNode("s");
            mc.convertAnother(((C2)o).getS());
            hsr.endNode();
            hsr.startNode("o3");
            mc.convertAnother(((C2)o).getO3());
            hsr.endNode();
        } else if(o instanceof C3) {
            hsr.startNode("s");
            mc.convertAnother(((C3)o).getS());
            hsr.endNode();
        }
    }
    public Object unmarshal(HierarchicalStreamReader hsr, UnmarshallingContext umc) {
        if(hsr.getNodeName().equals("C1")) {
            C1 o = new C1();
            while (hsr.hasMoreChildren()) {
                hsr.moveDown();
                if(hsr.getNodeName().equals("s")) {
                    o.setS(hsr.getValue());
                } else if(hsr.getNodeName().equals("o2")) {
                    o.setO((C2)umc.convertAnother(null, C2.class));
                } else if(hsr.getNodeName().equals("d")) {
                    // ignore d - we will set it later
                }
                hsr.moveUp();
            }
            o.setD(new Date());
            return o;
        } else if(hsr.getNodeName().equals("o2")) {
            C2 o = new C2();
            while (hsr.hasMoreChildren()) {
                hsr.moveDown();
                if(hsr.getNodeName().equals("s")) {
                    o.setS(hsr.getValue());
                } else if(hsr.getNodeName().equals("o3")) {
                    o.setO((C3)umc.convertAnother(null, C3.class));
                } else if(hsr.getNodeName().equals("d")) {
                    // ignore d - we will set it later
                }
                hsr.moveUp();
            }
            o.setD(new Date());
            return o;
        } else if(hsr.getNodeName().equals("o3")) {
            C3 o = new C3();
            while (hsr.hasMoreChildren()) {
                hsr.moveDown();
                if(hsr.getNodeName().equals("s")) {
                    o.setS(hsr.getValue());
                } else if(hsr.getNodeName().equals("d")) {
                    // ignore d - we will set it later
                }
                hsr.moveUp();
            }
            o.setD(new Date());
            return o;
        }
        return null;
    }
    public boolean canConvert(Class c) {
        return c.equals(C1.class) || c.equals(C2.class) || c.equals(C3.class);
    }
}

class C1 {
    private String s = "I am a C1";
    private Date d = new Date();
    private C2 o2 = new C2();
    public String getS() {
        return s;
    }
    public void setS(String s) {
        this.s = s;
    }
    public Date getD() {
        return d;
    }
    public void setD(Date d) {
        this.d = d;
    }
    public C2 getO2() {
        return o2;
    }
    public void setO(C2 o2) {
        this.o2 = o2;
    }
    public String toString() {
        return s + "/" + d + "/" + o2;
    }
}

class C2 {
    private String s = "I am a C2";
    private Date d = new Date();
    private C3 o3 = new C3();
    public String getS() {
        return s;
    }
    public void setS(String s) {
        this.s = s;
    }
    public Date getD() {
        return d;
    }
    public void setD(Date d) {
        this.d = d;
    }
    public C3 getO3() {
        return o3;
    }
    public void setO(C3 o3) {
        this.o3 = o3;
    }
    public String toString() {
        return s + "/" + d + "/" + o3;
    }
}

class C3 {
    private String s = "I am a C3";
    private Date d = new Date();
    public String getS() {
        return s;
    }
    public void setS(String s) {
        this.s = s;
    }
    public Date getD() {
        return d;
    }
    public void setD(Date d) {
        this.d = d;
    }
    public String toString() {
        return s + "/" + d;
    }
}
Avatar billede it-dyret Nybegynder
04. december 2007 - 11:31 #11
Endnu engang mange tak for det udførlige svar! Jeg har et sidste spørgsmål... Hvis man nu forestiller sig at klassen C3 er meget stor - og at man vil være helt fint tilfreds med at den bliver serialiseret ned ligesom XStream ville gøre som standard, er det så muligt? Eller er man nødt til at gøre det eksplicit i marshal og unmarshal metoderne ligesom i dit eksempel?

PS: Jeg håber ikke, at du føler at jeg driver rovdrift med uddybende spørgsmål...
Avatar billede it-dyret Nybegynder
04. december 2007 - 15:05 #12
Jeg har løst mit eget spørgsmål og er stort set tilfreds med løsningen, som du kan se nedenfor. Dog er der den lille detalje, at C1 instansen ikke bliver sat på C3 af XStream i det specielle eksempel, medmindre jeg selv sætter den til sidst (som man kan se ved TODO linien). Kan dette gøres på en anden måde i Converteren??

package tests;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.text.DateFormat;
import java.text.ParseException;

import java.util.Date;


public class XStreamSerializableAdvanced {
  public XStreamSerializableAdvanced() {
    C1 c1 = new C1();
    C2 c2 = new C2();
    C3 c3 = new C3(c1);
   
    c1.setC2(c2);
    c2.setC3(c3);
 
    System.out.println(c1);

    //default
    try {
      XStream xs1 = new XStream(new DomDriver());
      xs1.alias("C1", C1.class);
      xs1.alias("C2", C2.class);
      xs1.alias("C3", C3.class);
      OutputStream s1 = new FileOutputStream("C:\\XStream_default.xml");
      xs1.toXML(c1, s1);
      s1.close();
     
      XStream xs2 = new XStream(new DomDriver());
      xs2.alias("C1", C1.class);
      xs2.alias("C2", C2.class);
      xs2.alias("C3", C3.class);
      InputStream s2 = new FileInputStream("C:\\XStream_default.xml");
      C1 o2 = (C1)xs2.fromXML(s2);
      s2.close();
      System.out.println(o2);
    } catch (IOException e) {
      ;
    }

    // let us wait
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      ;
    }

    // special
    try {
      XStream xs3 = new XStream(new DomDriver());
      xs3.alias("C1", C1.class);
      xs3.alias("C2", C2.class);
      xs3.alias("C3", C3.class);
      xs3.registerConverter(new ProblemConverter());
      OutputStream s3 = new FileOutputStream("C:\\XStream_special.xml");
      xs3.toXML(c1, s3);
      s3.close();
     
      XStream xs4 = new XStream(new DomDriver());
      xs4.alias("C1", C1.class);
      xs4.alias("C2", C2.class);
      xs4.alias("C3", C3.class);
      xs4.registerConverter(new ProblemConverter());
      InputStream s4 = new FileInputStream("C:\\XStream_special.xml");
      C1 o4 = (C1)xs4.fromXML(s4);
      s4.close();
      System.out.println(o4);
     
      //TODO: KAN NEDENSTÅENDE GØRES I CONVERTEREN
      o4.getC2().getC3().setC1(o4);
      System.out.println(o4);
    } catch (IOException e) {
      ;
    }
  }

  public static void main(String[] args) throws IOException {
    new XStreamSerializableAdvanced();
  }


  class ProblemConverter implements Converter {
    public void marshal(Object o, HierarchicalStreamWriter hsr, MarshallingContext mc) {
      DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.FULL);

      if (o instanceof C1) {
        hsr.startNode("s");
        mc.convertAnother(((C1)o).getS());
        hsr.endNode();
       
        hsr.startNode("d");
        String date = formatter.format(((C1)o).getD());
        mc.convertAnother(date);
        hsr.endNode();
       
        hsr.startNode("C2");
        mc.convertAnother(((C1)o).getC2());
        hsr.endNode();
      } else if (o instanceof C2) {
        hsr.startNode("s");
        mc.convertAnother(((C2)o).getS());
        hsr.endNode();
       
        hsr.startNode("d");
        String date = formatter.format(((C2)o).getD());
        mc.convertAnother(date);
        hsr.endNode();
       
        hsr.startNode("C3");
        mc.convertAnother(((C2)o).getC3());
        hsr.endNode();
      }
    }

    public Object unmarshal(HierarchicalStreamReader hsr, UnmarshallingContext umc) {
      DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.FULL);
   
      if (hsr.getNodeName().equals("C1")) {
        C1 c1 = new C1();
        while (hsr.hasMoreChildren()) {
          hsr.moveDown();
          if (hsr.getNodeName().equals("s")) {
            c1.setS(hsr.getValue());
          } else if (hsr.getNodeName().equals("C2")) {
            c1.setC2((C2)umc.convertAnother(null, C2.class));
          } else if (hsr.getNodeName().equals("d")) {
            try{
              Date date = formatter.parse(hsr.getValue());
              c1.setD(date);
            } catch(ParseException pe){
              pe.printStackTrace();
            }
          }
          hsr.moveUp();
        }
        return c1;
      } else if (hsr.getNodeName().equals("C2")) {
        C2 c2 = new C2();
        while (hsr.hasMoreChildren()) {
          hsr.moveDown();
          if (hsr.getNodeName().equals("s")) {
            c2.setS(hsr.getValue());
          } else if (hsr.getNodeName().equals("C3")) {
            c2.setC3((C3)umc.convertAnother(null, C3.class));
          } else if (hsr.getNodeName().equals("d")) {
            try{
              Date date = formatter.parse(hsr.getValue());
              c2.setD(date);
            } catch(ParseException pe){
              pe.printStackTrace();
            }
          }
          hsr.moveUp();
        }
        return c2;
      }
      return null;
    }

    public boolean canConvert(Class c) {
      return c.equals(C1.class) || c.equals(C2.class);
    }
  }

  class C1 {
    private String s;
    private Date d;
    private C2 c2;
   
    public C1(){
      this.s = "I am a C1";
      this.d = new Date();
     
      System.out.println("C1 konstruktor");
    }

    public String getS() {
      return s;
    }

    public void setS(String s) {
      this.s = s;
    }

    public Date getD() {
      return d;
    }

    public void setD(Date d) {
      this.d = d;
    }

    public C2 getC2() {
      return c2;
    }

    public void setC2(C2 c2) {
      this.c2 = c2;
    }

    public String toString() {
      return "[" + hashCode() + "]/" + s + "/" + d + "/" + c2;
    }
  }

  class C2 {
    private String s;
    private Date d;
    private C3 c3;
   
    public C2(){
      this.s = "I am a C2";
      this.d = new Date();
     
      System.out.println("C2 konstruktor");
    }

    public String getS() {
      return s;
    }

    public void setS(String s) {
      this.s = s;
    }

    public Date getD() {
      return d;
    }

    public void setD(Date d) {
      this.d = d;
    }

    public C3 getC3() {
      return c3;
    }

    public void setC3(C3 c3) {
      this.c3 = c3;
    }

    public String toString() {
      return "[" + hashCode() + "]/" + s + "/" + d + "/" + c3;
    }
  }

  class C3 {
    private String s;
    private Date d;
    private C1 c1;
   
    public C3(C1 c1) {
      this.s = "I am a C3";
      this.d = new Date();
      this.c1 = c1;
     
      System.out.println("C3 konstruktor");
    }

    public String getS() {
      return s;
    }

    public void setS(String s) {
      this.s = s;
    }

    public Date getD() {
      return d;
    }

    public void setD(Date d) {
      this.d = d;
    }
   
    public C1 getC1(){
      return c1;
    }
   
    public void setC1(C1 c1){
      this.c1 = c1;
    }

    public String toString() {
      String s = null;
      if(c1 != null) {
        s = "" + c1.hashCode();
      }
     
      return "[" + hashCode() + "]/" + s + "/" + d + "/-->" +  s + "<--";
    }
  }
}
Avatar billede arne_v Ekspert
04. december 2007 - 15:06 #13
Saa skal du bare undlade at returnere true for den i canConvert metoden.

(der er ogsaa lidt overfloedig kode i marshal og unmarshal ...)
Avatar billede arne_v Ekspert
04. december 2007 - 15:08 #14
Hm. Det er jo naermest en cirkulaer reference.

Jeg tror at det du gjordt er det nemmeste.
Avatar billede it-dyret Nybegynder
04. december 2007 - 15:13 #15
Spørgsmålet er hermed lukket :) Tak for hjælpen
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester