XML mit Unterelementen

Dieses Thema im Forum "iOS-Developer" wurde erstellt von .holger, 27.06.10.

  1. .holger

    .holger Borowitzky

    Dabei seit:
    13.09.04
    Beiträge:
    8.967
    Moin moin,

    ich quäle mich mal wieder mit dem XMLParser.
    Ich habe eine XML Datei, die ich einlese… das klappt auch alles. Jedoch habe ich jetzt Unterelemente, die die gleichen Namen haben, wie die Oberelemente - man kann sich das wie Ordner vorstellen, die weitere Ordner beinhalten, die die selben Eigenschaften haben können wie die Oberordner.

    Also:


    Code:
    <element>
        <name>Hauptelement</name>
        <unterelement>
              <name>Unterelement</name>
        </unterelement>
    </element>

    Jetzt gehe ich die XML-Datei durch, lese die Elemente ein, lege für jedes Element ein neues Objekt in das Hauptarray, das u.a. "name" als String beinhaltet. Nun soll dazu ein weiteres Unterarray mit den Unterelementen angelegt werden, das dann in ebenfalls mit Objekten gefüllt und in das Hauptarray gelegt wird.

    Um die Elemente einzutragen mache ich das mit

    Code:
    if	([elementName isEqualToString:@"name"]){
    [einObjekt setValue:currentElementValue  forKey:elementName]; } 
    Leider ist "einObjekt" immer das Hauptobjekt aus dem Hauptarray und somit bekommt dann das Objekt "Hauptelement" in diesem Zug den Namen "Unterelement" zugewiesen, obwohl das natürlich quatsch ist.

    Wie kann ich dies am verhindern, ohne die XML Datei zu verändern - ich möchte die Möglichkeit haben die Elemente unendlich weit zu untergliedern… Ich muss doch irgendwie herausbekommen können, ob ich in der zweiten Ebene von Elementen bin oder so…
     
  2. hillepille

    hillepille Echter Boikenapfel

    Dabei seit:
    19.07.09
    Beiträge:
    2.374
    hi, ich habe zwar keine ahnung von os - entwicklung, aber könnte es daran liegen, dass dein unterelement auf der gleichen ebene wie das eigentlich übergeordnete element name steht und nicht unterlhalb con name? also

    <name>
    <unterelement>
    <name></name>
    </unterelement>
    </name>

    a, mea culpa, ich hatte überlesen, dass das xml-file unberührt bleiben soll. wenn nach einem hauptelement immer das unterelement kommt bzw. kommen kann, dann könntest du ggf. dem gerade erstellten objekt einobjekt doch das unterlement zuweisen.

    if ([elementName isEqualToString:mad:"unterelement"]){
    [einObjekt setValue:currentElementValue forKey:unterelement]; }

    wie gesagt, sprachentechnisch kann ich nicht mitreden, aber so in der art würde ich das angehen.
     
    #2 hillepille, 27.06.10
    Zuletzt bearbeitet: 27.06.10
  3. .holger

    .holger Borowitzky

    Dabei seit:
    13.09.04
    Beiträge:
    8.967
    das mache ich, ich gehe die XML durch, wenn ich ein Unterelement Start finde, dann erstelle ich ein neues Objekt, und möchte anfangen dies zu füllen, wenn dann das End-Tag vom Unterelement gefunden wird, dann wird dieses neue Objekt als Element vom ersten Objekt gespeichert… nur der Parser versteht halt nicht, dass wenn er "name" findet, dies zum Unterelement gehört und nicht zu Hauptelement…
     
  4. .holger

    .holger Borowitzky

    Dabei seit:
    13.09.04
    Beiträge:
    8.967
    hmmm… zur Not lass ich nen Counter mitlaufen, der zählt wie tief ich in der Verschachtelung bin, aber das kann doch auch nicht die elegante Lösung sein, oder?
     
  5. MacApple

    MacApple Schmalzprinz

    Dabei seit:
    05.01.04
    Beiträge:
    3.583
    Du könntest das über einen Stack lösen. Immer wenn ein neues Element anfängt, schiebst Du das auf Deinen "Bearbeitungsstack" und arbeitest dann halt immer mit dem obersten Element. Kommt dann das Ende des Elements, nimmst Du es wieder vom "Bearbeitungsstack" runter. Durch das auf den Stack legen und wieder runter nehmen, wechselst Du dann quasi automatisch zwischen den Ebenen hin und her.

    MacApple
     
  6. Poljpocket

    Poljpocket Salvatico di Campascio

    Dabei seit:
    07.01.07
    Beiträge:
    432
    So wie sich das anhört, benutzt du die "Event-driven XML parsing" Methode von Cocoa. Es gibt aber noch eine zweite, nämlich "Tree-based XML parsing". Ich verwende für meine Datenbanken fast nur die, weil du den Baum der Objekte schön mit einer Baumstruktur von 'NSXMLNode'-Instanzen erhälst. Diese enthalten immer automatisch child-nodes, also genau die Unterelemente, die du suchst. Jede node enthält auch automatisch alle Attribute (also damit deinen 'name'-Tag). Das mühsame Einlesen von allen Elementen und dessen Attribute wird dir damit erspart.

    Programming Guide: Tree-Based XML Programming Guide

    Gruss ppocket
     
    .holger gefällt das.
  7. denio

    denio Empire

    Dabei seit:
    23.01.10
    Beiträge:
    88
    Hab neulich das selbe Problem gehabt, hab es durch ein kleinen "Workaround" gelöst, ist zwar nicht ganz so schön, aber da es bei mir maximal einen Unterbaum gibt, ist es ok. So kommt z.B. Type zweimal vor jeweils im "Haupt"- und "Unter"-Baum

    Code:
    if ([elementName isEqualToString:kName_Type])
    {
    // emptyCheck ist ein kleine Hilfsmethode die guckt ob "currentData.typeDataMain/Sub" leer ist, wenn ja handelt es
    // sich um typeDataMain aus dem Hauptbaum
    	if (emptyCheck(currentData.typeDataMain))
    	{
    // Für den Fall ausgelesene XML-Wert (currentString) der typeDataMain leer ist wird ein emptyString 
    // eingefügt somit typeDataMain beim nächsten durchlauf nicht mehr empty und emptyCheck geht in
    // den else-Zweig
    		if ([@"" isEqualToString:currentString])
    		{
    			currentData.typeDataMain = emptyString;
    		}
    		else
    		{
    // wenn currentString nicht leer ist wird dieser XML-Wert nun typeDataMain zugewiesen und
    // und typeDataMain ist beim nächsten durchlauf nicht mehr empty -> emptyCheck geht in den
    // else-Zweig
    			currentData.typeDataMain = currentString;
    		}
    	}
    	else if (emptyCheck(currentData.typeDataSub))
    	{
    // selbes Prinzip wie oben, aber nun wird typeDataSub mit nem emptyString oder currentString gefüllt
    		if ([@"" isEqualToString:currentString])
    		{
    			currentData.typeDataSub = emptyString;
    		}
    		else
    		{
    			currentData.typeDataSub = currentString;
    		}
    	}
    }
    Ich weis ist nicht wirklich schick, aber momentan reicht es, ohne alles für den Tree-Based XML-Parser umzuschreiben :)
     
  8. Oxy

    Oxy Antonowka

    Dabei seit:
    15.03.07
    Beiträge:
    363
    Ich würde es mit einem Switch und einem Boolwert lösen.
    String tag="";
    bool isSub=False;
    <Main>
    tag="Main";
    <name>
    tag="name";
    <unterelement>
    isSub=True;
    tag="unterelement"
    <name>
    tag="name";
    </name>
    </unterelement>
    </name>
    <Main>


    Switch(tag)
    case "name":
    if(isSub)
    {...}else{...}
     
    .holger gefällt das.
  9. .holger

    .holger Borowitzky

    Dabei seit:
    13.09.04
    Beiträge:
    8.967
    Hey,

    danke. Hab das jetzt erstmal über nen Booleanwert gelöst. Das reicht vorerst, da ich bisher nur in eine Unterebene gehe und das Programm schnell fertig werden muss.

    Werde mir aber die Treebased-Sache mal angucken und dann in einem Update das ganze wohl umstricken… klingt sauberer…

    Gruß Holger
     
  10. .holger

    .holger Borowitzky

    Dabei seit:
    13.09.04
    Beiträge:
    8.967
    Hmm…*ich versuch die Unterelemente gerade wieder auszugeben…*ist aber noch etwas komplizierter als bisher gesagt.

    Also ich habe jetzt ein Objekt. In dem Objekt ist u.a. ein Array aus anderen Objekten. Und nun brauche ich ein bestimmtes Element aus diesen letzten Objekten.

    for (id obj in MeinArray.DieObjekte)
    {
    NSLog(@"obj: %@", obj.Name);
    }

    das funktioniert jedoch nicht, da "Name" innerhalb der Schleife nicht definiert ist, obwohl es ein Element von "DieObjekte" ist.
    Irgendwie steh ich auf dem Schlauch… vielleicht sollte ich morgen weitermachen…
     
  11. MacApple

    MacApple Schmalzprinz

    Dabei seit:
    05.01.04
    Beiträge:
    3.583
    Laut Deinem Code oben muss das auch
    Code:
    obj.name
    heißen. Du solltest Dich auch an die Namens-Konventionen von Cocoa halten. Macht das Programiererleben leichter.

    MacApple
     
  12. .holger

    .holger Borowitzky

    Dabei seit:
    13.09.04
    Beiträge:
    8.967
    Hey, meine Variable heißt schon nicht 'name' - das hab ich hier nur so reingeschrieben um es einfach zu halten.

    Ich hab grad festgestellt, dass ich mit

    Code:
    	for (id obj in meinArray.DieObjekte)
    		
    	{
    		NSString *test = [obj name];
    		NSLog(@"%@",test);
    }
    korrekt an das Ergebnis komme…
     
  13. MacApple

    MacApple Schmalzprinz

    Dabei seit:
    05.01.04
    Beiträge:
    3.583
    Das hilft natürlich ungemein beim Helfen, wenn Du hier anderen Code rein schreibst, als Du in Deinem Projekt hast.

    Ach, da schau her. Dir ist bekannt, dass der Compiler aus
    Code:
    [obj name]
    und
    Code:
    obj.name
    den gleichen Code generiert?

    Code:
    obj.name
    und
    Code:
    obj.Name
    hingegen sind für den Compiler zwei verschiedene Dinge.

    MacApple
     
  14. .holger

    .holger Borowitzky

    Dabei seit:
    13.09.04
    Beiträge:
    8.967
    ja, das ist mir bekannt und deswegen wundert es mich…
    Ich kann leider den echten Code hier nicht reinschreiben… sorry