XML String auswerten

Klingone22

Level-2
Beiträge
100
Reaktionspunkte
14
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo alle miteinander!

Ich habe ein Problem, was vermutlich auf meine Unwissenheit zurückzuführen ist und bei manchen Kopfschütteln und/oder Augenverdrehen auslösen wird, also bitte um Nachsicht :rolleyes::

Ich habe einen String, wo ich per HTTP einen XML String (größer als 512 Zeichen) empfange:
Code:
    sContent            : STRING(4000);


IF NOT fbRequest.bError THEN                 
    bGetContentResult:=     fbRequest.GetContent(pContent:= ADR(sContent),
                            nContentSize:= SIZEOF(sContent),
                            bSetNullTermination:= TRUE);
    IF fbRequest.nStatusCode >= 200 AND fbRequest.nStatusCode < 300  THEN
        bGetJsonResult:= FALSE;

Der empfangene String schaut auszugsweise wie folgt aus:

Code:
scontent:= ''<?xml version="1.0" encoding="UTF-8" standalone="yes"?><socketsState><socketState><id>672C24E1-780D-457B-BAA1-C1D3BB5A7D2B.476A5CEA-951D-436A-A6CC-F71B94E48725.4A2963B9-2831-4656-A9E7-328BA8490F52</id><name>EVSE.PLUG A.SOCKET A</name><number>1</number><state>8</state><previousState>6</previousState>......'

Nun suche ich eine einfache Methode, um z.B. <state> auszuwerten. Die Variante mit "find2" und "memcpy" und "right" geht, finde ich aber nicht schön.

Mit FB_JsonDomParser bin ich leider nicht erfolgreich (jsonDoc bleibt immer 0):

Code:
    bGetJsonResult        : BOOL;
    jsonDoc                : SJsonValue;
    fbJson                : FB_JsonDomParser;


bGetJsonResult:= FALSE;
jsonDoc:= fbRequest.GetJsonDomContent(fbJson);
    IF jsonDoc <> 0 THEN
        bGetJsonResult:= TRUE;
            IF fbJson.HasMember(jsonDoc, 'state')  THEN


Hab auch ein wenig mit FB_XmlDomParser und laut dem Infosys Beispiel experimentiert, aber auch leider nicht erfolgreich.

FB_XmlSrvRead kann ja nur eine Datei verarbeiten, da ich ja "nur" einen String habe, funktioniert das ja nicht.


Ich vermute, dass ich irgendeine Kleinigkeit falsch mache bzw. eine Grundlage nicht verstehe / falsch interpretiere.

Danke für eure Hilfe!

LG

Klingone22
 
Ja, in der Varialbe sContent ist der String ersichtlich:

1741716440627.png


Ich habe es nun mit einen FB gelöst, der am Eingang den sContent und die zu suchende Variable bekommt, und ma Ausgang int / real / string liefert. Geht sicher einfacher / schöner, aber funktioniert.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Guga!

mit fbJson.HasMember findet der Code den Inhalt nicht. Mit jsonProp hab ich es noch nicht probiert, werde es (wenn ich Zeit finde) damit nochmals probieren. Danke für den Hinweis.

Ich habe es wie folgt gelöst, aber es geht sicher auch schöner / eleganter / recourcenschonender:


Code:
FUNCTION_BLOCK fb_ReadXML
VAR_INPUT
    str_content        :  STRING(4000);         // content to search through
    str_value            :  STRING(40);            // string to find the value to
END_VAR
VAR_OUTPUT
    int_value_out        : INT;
    real_value_out        : REAL;
    str_value_out        : STRING(50);  
END_VAR
VAR
    dint_contet_length: DINT;
    udint_find: UDINT;
    str_temp: STRING(4000);
    int_find: INT;
    str_temp2: STRING(255);
END_VAR


int_value_out    := 0;
real_value_out    := 0;
str_value_out    := '';


// find position of state
dint_contet_length := UDINT_TO_DINT(len2(ADR(str_content)));

udint_find := find2(ADR(str_content),ADR(str_value));

// delete int_find characters to the left
memcpy(ADR(str_temp),ADR(str_content)+udint_find+INT_TO_UDINT(len(str_value))-1,DINT_TO_UINT(dint_contet_length)-UDINT_TO_UINT(udint_find));

   
//copy the found string to the outputs
int_find := find(str_temp,'<');
str_temp2 := left(str_temp,int_find-1);
int_value_out := STRING_TO_INT(str_temp2);
real_value_out:= STRING_TO_REAL(str_temp2);
str_value_out:= str_temp2;


LG Kingone22
 
Hallo!

Ich habe es mir nun nochmals angeschaut mit Tc3JsonXmlSampleJsonSaxReader und Tc3JsonXmlSampleXmlDomReader. Von der Beschreibung müsste der Tc3JsonXmlSampleJsonSaxReader funktioneren:
So schaut meine Information im Brower aus:

XML:
<chargePointsState>
    <chargePointState>
        <id>672C24E1-780D-457B-BAAB-C1D3BB5A7D2B</id>
        <name>EVSE</name>
        <state>0</state>
        <previousState>-100</previousState>
        <error>0</error>
        <user/>
        <userType/>
        <composedVendorErrorCodes/>
    </chargePointState>
</chargePointsState>

mein sContent beinhaltet genau diese Informationen in einen String:
Code:
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?><chargePointsState><chargePointState><id>672C24E1-780D-457B-ABBA-C1D3BB5A7D2B</id><name>EVSE</name><state>0</state><previousState>-100</previousState><error>0</error><user></user><userType></userType><composedVendorErrorCodes></composedVendorErrorCodes></chargePointState></chargePointsState>'


Wenn ich es genau wie im Beispiel mache, funktioniert es nicht:


Code:
xmlDoc :=  fbXml.ParseDocument(sContent);
            
(* Parse XML nodes - Option 3 *)
xmlMachines := fbXml.ChildByName(xmlDoc, 'previousState');
xmlIterator := fbXml.Begin(xmlMachines);
WHILE NOT fbXml.IsEnd(xmlIterator) DO
  xmlMachineNode := fbXml.Node(xmlIterator);
  xmlMachineNodeValue := fbXml.NodeText(xmlMachineNode);
  xmlIterator := fbXml.Next(xmlIterator);
END_WHILE

Ich hab ja eine funktionieren Lösung, möchte es aber nur verstehen, wie es richtiger sein könnte.

Danke!

LG Kingone22
 
Zuviel Werbung?
-> Hier kostenlos registrieren
dein XML-Encoding ist strukturiert. 'previousState' ist auf der Ebene 3.
chargePointsState->chargePointsState->previousState

Du musst dich entsprechend auf die korrekte Ebene dich durchhangeln bevor du in dem Child-Bereich den 'previousState' findest.
 
Hallo Guga!

Danke für den Hinweis mit dem durchhangeln (ich hab keine XML Erfahrung bis jetzt, daher wird das für fille logisch sein, leider nicht für mich). So funktionierts nun, wobei ich nicht verstehe was die WHILE Schleife genauch macht:


Code:
    sContent                : STRING(4000);
 
    fbXml                      : FB_XmlDomParser;
    xmlDoc                     : SXmlNode;
      xmlIterator             : SXmlIterator;
    xmlNode                     : SXmlNode;
    nValue                    : DINT;   
    bParseNow                : BOOL;

__________________________________________________________________________________
xmlDoc :=  fbXml.ParseDocument(sContent);
    
IF bParseNow  THEN

    (* Parse XML nodes - Option 3 *)
    xmlNode := fbXml.ChildByName(xmlDoc, 'chargePointsState');   
    xmlNode := fbXml.ChildByName(xmlNode, 'chargePointState');
    xmlNode := fbXml.ChildByName(xmlNode, 'previousState');                   
    xmlIterator := fbXml.Begin(xmlNode);
    WHILE NOT fbXml.IsEnd(xmlIterator) DO
          xmlNode := fbXml.Node(xmlIterator);
         xmlIterator := fbXml.Next(xmlIterator);
        nValue := fbXml.AttributeAsInt(xmlNode);
    END_WHILE   
        
    bParseNow := FALSE; // Prevent re-parsing unless triggered again
END_IF

Danke nochmals für die Hilfe!
 
Die While-Schleife ist in diesem Fall overkill. So etwas nutzt man wenn die XML-Stuktur dynamisch aufgebaut ist.
Hier wird in der Ebene jeder XML-Knoten angeschaut und gelesen. Macht bei dir nicht wirklich Sinn aber da die Schleife eh nach dem ersten Durchlauf abgebrochen wird entsteht auch kein Schaden :)
Du könntest in deinem Fall auch direkt dir den INT-Wert ausgeben lassen mit fbXml.AttributeAsInt(xmlNode);
 
Zurück
Oben