1

EDIT:

I messed around some time now and came to the following XML result:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<List>
    <client name="Robert">
        <fileNames name="anleitung.pdf"/>
        <fileNames name="fernseher.jpg"/>
        <fileNames name="pikantesfoto.jpg"/>
    </client>
    <client name="Jakob">
        <fileNames name="fernseher.jpg"/>
        <fileNames name="pikantesfoto.jpg"/>
        <fileNames name="tagebuch.txt"/>
    </client>
</List>

This isn't really a many to many relationship, but it's close, I need to remove the data redunance.

The "fileNames" should be single elements which and the clients should point on it. So if there is one file that has the same name of a file from another clients, it should appear only once.

This are my classes:

Marshaller: public class XMLMarshaller {

private Pool pool;

public XMLMarshaller(Pool pool) {
    this.pool = pool;
}

public void marshal() {

    JAXBContext jc;
    try {
        jc = JAXBContext.newInstance(Pool.class, Client.class, FileName.class);

        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Save XML-File");
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XML-Document", "*.xml"));

        File path = fileChooser.showSaveDialog(new Stage());
        if (path.toString().endsWith(".xml") == false) {
            path = new File(path.toString() + ".xml");
        }
        if (path.isFile() == false)
            path.createNewFile();

        FileOutputStream fos = new FileOutputStream(path);
        OutputStreamWriter xmlOut = new OutputStreamWriter(fos, Charset.forName("UTF8"));

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        QName qName = new QName("List");
        JAXBElement<Pool> jaxbElement = new JAXBElement<>(qName, Pool.class, pool);
        marshaller.marshal(jaxbElement, xmlOut);

        xmlOut.flush();
        xmlOut.close();
    } catch (Exception e) {e.printStackTrace();}
}

}

Pool (Contains a list of all existing "FileNames" and Clients):

public class Pool {

private List<FileName> fileNames_pool;
private List<Client> clients;

public Pool() {
    this.fileNames_pool = new ArrayList<>(20);
    this.clients = new ArrayList<>(20);
}

public List<FileName> getFileNameList() {
    return this.fileNames_pool;
}

public List<FileName> getFileNames_pool() {
    return this.fileNames_pool;
}

@XmlAnyElement(lax = true)
public List<Client> getClientList() {
    return this.clients;
}


public boolean addClient(String clientName) {
    this.clients.add(new Client(clientName));
    return true;
}

public boolean addFileName(int clientIndex, String fileName) {

    int foundIndex = 0;
    boolean foundOne = false;

    for (int i=0; i<fileNames_pool.size(); ++i) {
        if (fileNames_pool.get(i).name == fileName) {
            foundIndex = i;
            foundOne = true;
            break;
        }
    }

    if (foundOne) {
        clients.get(clientIndex).addFileName(fileNames_pool.get(foundIndex));
    } else {
        FileName temp = new FileName(fileName);
        fileNames_pool.add(temp);
        clients.get(clientIndex).addFileName(temp);
    }
    return true;
}

}

Client (there will be multiple instances/objects of it): @XmlRootElement public class Client {

static int numberOfClients = 0;

private int id;

@XmlAttribute
public String name;

public List<FileName> fileNames = new ArrayList<>();

public Client() {

}

public Client(String name) {
    this.name = name;
    this.id = numberOfClients++;
}

@XmlElement
public List<FileName> getFileNames() {
    return this.fileNames;
}

public void addFileName(FileName fileName) {
    this.fileNames.add(fileName);
}

}

FileNames (there will also be multiple instances of it):

//@XmlRootElement
public class FileName {

//  public static int instanceCounter = 0;
//  
//  @XmlIDREF
//  public int idref = 0;

    @XmlAttribute
    public String name;

    public FileName() {

    }

    public FileName(String name) {
        this.name = name;
//      idref = instanceCounter++;
    }
}

You will see that I sometimes really messed things up, but I'm totally out of ideas on what could work and what not. The Pool class is something like a Wrapper as Blaise Doughan told me to do, if I didn't get him wrong. And what I totally didn't get: Why should there be an "id" based on the string class?

codepleb
  • 10,086
  • 14
  • 69
  • 111

2 Answers2

1

Here is an example of how to map POJO's to XML using jaxb http://www.mkyong.com/java/jaxb-hello-world-example/

However in your case you have a bi-directional relationship, your ClientName class has a reference to FileName class, and FileName has a reference back to ClientName. In the case of bi-directional XML mapping using JAXB you will want to use the @XmlTransient tag on one of the relationships. @XmlTransient may not be what you are looking for because for whichever field is annotated, it will be removed from that direction. There is a solution to this using the afterUnmarshal method. http://www.tutorialspoint.com/java/xml/javax_xml_bind_unmarshaller.listener_afterunmarshal.htm

Or you can use MOXy's @XmlInverseReference to handle the unmarshalling for you https://www.eclipse.org/eclipselink/documentation/2.4/moxy/shared_reference_relationships005.htm

j.con
  • 879
  • 7
  • 19
1

JAXB Requirement

Each object must be reference through a containment/nesting relationship such as @XmlElement, @XmlElementRef, or @XmlAnyElement(lax=true).

Non-Containment Relationships

Once you have a tree as formed by the above JAXB requirement you can form intra-document relationships using @XmlID/@XmlIDREF (see: http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html). This is where the following statement you made in your question comes from:

In my understanding it should work, if I just keep 2 Lists in both classes that point to each other, but I read in the internet, that a 3rd class should be involved to map this 2 classes in a many to many relationship.

What Can You Do?

Take a look at this answer I gave to another question. In that answer I demonstrate how you can leverage an XmlAdapter to represent the first occurrence of an object through containment and subsequent references through a reference.

Community
  • 1
  • 1
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Hello Blaise :) I'm now on it to get what you mean (since ~1 hour). My english sadly isn't the best. I know, that it's very little beside the UML standard, but do you mean something like this? http://www.bilder-hochladen.net/files/big/1fby-6q-0116.png Tomorrow (I hope) I will look closer to your second link. It seems that I also have to get a little bit more into the world of "databases". Thank you very much for your work here! :) – codepleb Mar 19 '14 at 22:11
  • I edited my main question totally and would be really glad, if you could give a look on it Blaise. The XML will work, but I never got to the result I was looking for. Greets, Trudler – codepleb Mar 26 '14 at 21:42