Witam,
Potrzebuje wydobyć treść znacznika lokalnyId oraz sprawdzić stan składni w przypadku syntax error
z (przykładowej) składni pliku xml.
<gml:FeatureCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:ot="urn:gugik:specyfikacje:gmlas:bazaDanychObiektowTopograficznych10k:2.0" gml:id="GUGiK_OT_KUMN_A" xsi:schemaLocation="urn:gugik:specyfikacje:gmlas:bazaDanychObiektowTopograficznych10k:2.0 ../XSD/BDOT10k_BDOO.xsd">
<gml:featureMember>
<ot:OT_KUMN_A gml:id="id_272D6AAF-DB0F-9B0E-E053-CC2BA8C0B5EA">
<ot:lokalnyId>272D6AAF-DB0F-9B0E-E053-CC2BA1testPB</ot:lokalnyId>
<ot:przestrzenNazw>PL.PZGiK.337.BDOT10k</ot:przestrzenNazw>
<ot:wersja>2015-07-30T00:00:00</ot:wersja>
<ot:poczatekWersjiObiektu>2015-07-30T00:00:00</ot:poczatekWersjiObiektu>
<ot:oznaczenieZmiany>...</ot:oznaczenieZmiany>
<ot:zrodloDanychGeometrycznych>...</ot:zrodloDanychGeometrycznych>
<ot:geometria>
gml:id="id-e597f8e9-a1cd-458e-9442-c32a7e4fc280-0" srsName="EPSG:2180" srsDimension="2">
<gml:exterior>
<gml:LinearRing>
<gml:posList>279700.74 397225.41 ... 279655.74 397225.41 279655.74 397225.41</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</ot:geometria>
</ot:OT_KUMN_A>
</gml:featureMember>
Z takiego pliku mam dwa przypadki do wykonania:
- Sprawdzenie walidacji.
Dzieje się to kodem:
plikGML = pliki[int(task.description())]
try:
walidowanyPlik = lxml.etree.parse(plikGML)
nazwa = plikGML[-13:-4]
print (nazwa)
unique_lokalnyId = set()
plikiZparsowane.append(plikGML) # przechowujemy pliki, które są na tyle dobre by wykonać ew. kontrole atrybutowe
ns = {'bt': 'urn:gugik:specyfikacje:gmlas:modelPodstawowy:1.0'}
try:
if walidowanyPlik.xpath('//*[namespace-uri()="%s"]' % ns['bt'], namespaces=ns):
raise OSError("Plik nie jest zgodny z obowiązującym schematem aplikacyjnym GML - z plikiem XSD. Plik wykorzystuje przestrzeń nazw Modelu Podstawowego, która była wykorzystywana w schematach aplikacyjnych przed 2021 rokiem.")
except:
pass
root = walidowanyPlik.getroot()
if root != None:
wynikWalidacji = xmlschema.validate(walidowanyPlik)
if not wynikWalidacji:
walidacjaZWynikiemPozytywnym = False
element=walidowanyPlik.iter()
elements_list = list(element)
zakresy = []
for element in elements_list:
if element.tag.endswith('featureMember'):
start_line = element.sourceline
end_line = element.getnext().sourceline if element.getnext() else float('inf')
zakresy.append((start_line, end_line))
for error in xmlschema.error_log:
line_number = error.line
if line_number:
found_in_range = False
for start_line, end_line in zakresy:
if start_line <= line_number <= end_line:
found_in_range = True
for element in elements_list:
if start_line <= element.sourceline <= end_line:
ot_elements = element.xpath('.//*[contains(name(), "lokalnyId")]', namespaces=ns)
for ot_element in ot_elements:
if ot_element.sourceline and ot_element.sourceline <= line_number: # błąd jest poniżej
lokalnyId = ot_element.text
if lokalnyId:
unique_lokalnyId.add(lokalnyId)
break
elif ot_element.sourceline > line_number: # błąd linii jest poniżej
lokalnyId = ot_element.text
if lokalnyId:
unique_lokalnyId.add(lokalnyId)
break
break
print("Unikalne lokalne ID:", unique_lokalnyId) #typ set
Finalne unique_lokalnyId jest prawidłowy i trafia do:
return {'taskID':task.description(), 'plikGML':plikGML, 'error':xmlschema.error_log, "lokalnyId":unique_lokalnyId}
, ale potrzebuje z tego seta wydobyć tak, by każdy wpisywał się oddzielnie do kolumny excela lub pdf-a odpowiadającemu wierszowi dla 3 pozostałych wartości w return
w tym celu szef mój napisał coś takiego:
for i in range(len(value['error'])): #value to powyższy return, gdyż jest to w oddzielnej funkcji
# for j in value['lokalnyId']:
# lokalnyId_df.append(str(j))
opisBledu = value['error'][i].message
for key in frazy:
opisBledu = opisBledu.replace(key,frazy[key]) # tłumaczenie- tablica powyżej
msg = 'Walidacja pliku: ' + str(value['plikGML']) + ' z wynikiem negatywnym.\n' + \
'- ' + config['KodyWalidacji'][value['error'][i].type_name] + '\n' + \
'- wiersz: ' + str(value['error'][i].line) + '\n' + \
'- komunikat błędu: ' + opisBledu + '\n\n'
if formatPlikuRaportu == 'txt':
plikRaportu.write(msg)
else:
walidowanePliki_df.append(str(value['plikGML']))
wiersze_df.append(str(value['error'][i].line))
opisyBledow_df.append(config['KodyWalidacji'][value['error'][i].type_name])
komunikatyBledow_df.append(opisBledu)
lokalnyId_df.append(str(value['lokalnyId']))
a ja muszę dodać dodać do tego kodu jeszcze wartości z unique_lokalnyId (set) lub lokalnyId, aby prawidłowo dodawały się do tablicy:
bledyWalidacji = {'WALIDOWANY PLIK': walidowanePliki_df, 'WIERSZ': wiersze_df, 'OPIS BŁĘDU': opisyBledow_df, 'KOMUNIKAT BŁĘDU': komunikatyBledow_df, 'LOKALNY ID OBIEKTU':lokalnyId_df} wykorzystywanej później do tworzenia raportu w excelu i adobe.
Obecnie w pierwszej wyświetlają się nieprawidłowo (dublowanie ostatniej wartości seta), a drugiej nie pozwala z powodu różnic długości danych
- Obsługa przypadku (również ze wpisaniem lokalnyId)
except lxml.etree.XMLSyntaxError as error: oraz except OSError as error:
Tutaj Gemini AI zaproponowało mi sprawdzanie z patternu:
except lxml.etree.XMLSyntaxError as error:
error_message = str(error) # zmiana typu błędu na string
walidacjaZWynikiemPozytywnym = False
print (error_message)
first_digit = re.search(r'\d', error_message).group()
# Wydobycie linii z frazy
line_numbers = re.findall(r'line (\d+)', error_message)
line_number = int(line_numbers[0]) if line_numbers else None
print("Numer linii:", line_number)
if line_number:
# Odczytanie zawartości pliku GML (np. 'plik.gml')
with open(plikGML, 'r') as file:
gml_content = file.readlines()
# Wyszukiwanie najbliższego lokalnyId
lokalnyId = None
i = line_number - 1
pattern = re.compile(r'(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})')
while lokalnyId is None and i >= 0:
match = pattern.search(gml_content[i])
if match:
lokalnyId = match.group(1)
i -= 1
# Szukanie lokalnyId w dół od linii z błędem (jeśli nie znaleziono wcześniej)
if not lokalnyId:
for i in range(line_number, len(gml_content)):
match = pattern.search(gml_content[i])
if match:
lokalnyId = match.group(1)
break
# Sprawdzenie, czy lokalnyId znajduje się w znacznikach <ot:lokalnyId>
if lokalnyId and not lokalnyId.startswith('<ot:lokalnyId>') and not lokalnyId.endswith('</ot:lokalnyId>'):
lokalnyId = None
print("Znaleziony lokalnyId:", lokalnyId)
return {'taskID':task.description(), 'plikGML':plikGML, 'error':error, "lokalnyId":lokalnyId}
tyle że podaje błędne, bo jak można spojrzeć na strukturze załączonej na początku pattern (\w{8}-\w{4}-\w{4}-\w{4}-\w{12}) występuje dwukrotnie, co spowodowało konieczność rozbicia na przypadki (moja robota):
with open(plikGML, 'r') as file:
gml_content = file.readlines()
i = line_number - 1
pattern = re.compile(r'(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})')
pattern_fm = re.compile(r'[<^]gml:featureMember[^>]')
pattern_geometria_start = re.compile(r'<[^>]*:geometria>')
pattern_geometria_end = re.compile(r'</[^>]*:geometria>')
pattern_partial_geometria = re.compile(r'<gml:(Polygon|Point|Curve)[^>]', re.IGNORECASE)
inside_fm = False
inside_geometria = False
lokalnyId = None
while lokalnyId is None and i >= 0:
# Szukanie lokalnyId w górę od linii z błędem
#for i in range(line_number - 1, -1, -1):
if pattern_geometria_start.search(gml_content[i]) or pattern_partial_geometria.search(gml_content[i]):
inside_geometria = True
if pattern_geometria_end.search(gml_content[i]):
inside_geometria = False
# if not inside_geometria:
# if pattern_fm.search(gml_content[i]):
# inside_fm = True
# elif pattern_fm.search(gml_content[i]) and not inside_fm:
# break
if not inside_geometria: # not inside_fm and
match = pattern.search(gml_content[i])
if match:
lokalnyId = match.group(1)
print ("traf", lokalnyId)
break
i -= 1
# Szukanie lokalnyId w dół od linii z błędem (jeśli nie znaleziono wcześniej)
if not lokalnyId:
for i in range(line_number, len(gml_content)):
if pattern_geometria_end.search(gml_content[i]):
inside_geometria = False
if pattern_geometria_start.search(gml_content[i]) or pattern_partial_geometria.search(gml_content[i]):
inside_geometria = True
if not inside_geometria:
# if pattern_fm.search(gml_content[i]):
# inside_fm = True
# elif pattern_fm.search(gml_content[i]) and not inside_fm:
# break
# if not inside_fm:
match = pattern.search(gml_content[i])
if match:
lokalnyId = match.group(1)
print ("znal", lokalnyId)
czyli żeby w przypadku znalezienia błędu składniowego wewnątrz 'geometria' (przedrostki jak tutaj ot: mogą być różne!) szukał w górę, podobnie jeśli znajdzie błąd poniżej niego, ale powyżej gml:featureMember (tutaj już stały przedrostek)-> W skrócie chodzi, by w przypadku dowolnego błędu synthax (składniowego) pliku xml ma szukać lokalnegoId wewnątrz danego gml:featureMember (jeśli w tej frazie też jest błąd to między poprzednim poprawnym, a następnym).
Z góry dziękuje za wszelką pomoc!
PS. znacznik (przykładowy tutaj) OT_KUMN_A zmienia się w zależności od nazwy pliku (ostatnie 9 znaków)!