Reputation: 103
I have a dictionary like string as below:
str = "Access AR1:\n\tTargets: \n\t\tManagement Name:csw_1\n\t\tObject Name:csw_obj_1\n\t\tdetails:103\n\t\tManagement Name:csw_123\n\t\tObject Name:csw_obj_134\n\t\tdetails:123\n\tSources: \n\t\tIP:10.20.30.40\n\t\tSubnet Mask:255.255.255.255\nAccess AR2:\n\tTargets: \n\t\tManagement Name:csw_2\n\t\tObject Name:csw_obj_2\n\t\tdetails:110\n\t\tManagement Name:csw_431\n\t\tObject Name:csw_obj_21\n\t\tdetails:134\n\tSources: \n\t\tIP:10.20.10.10\n\t\tSubnet Mask:255.255.255.192"
and this goes on in the same format, which would look like:
Access AR1:
Targets:
Management Name:csw_1
Object Name:csw_obj_1
details:103
Management Name:csw_123
Object Name:csw_obj_134
details:123
Sources:
IP:10.20.30.40
Subnet Mask:255.255.255.255
Access AR2:
Targets:
Management Name:csw_2
Object Name:csw_obj_2
details:110
Management Name:csw_431
Object Name:csw_obj_21
details:134
Sources:
IP:10.20.10.10
Subnet Mask:255.255.255.192
and this need to be changed to:
str = {"Access AR1": { "Targets": [{"Management Name:csw_1", "Object Name":"csw_obj_1", "details":"103"}, {"Management Name:csw_123", "Object Name":"csw_obj_134", "details":"123"}]
"Sources": {"IP":"10.20.30.40", "Subnet Mask": "255.255.255.255"}
},
"Access AR2": { "Targets": [{"Management Name:csw_2", "Object Name":"csw_obj_2", "details":"110"}, {"Management Name:csw_431", "Object Name":"csw_obj_21", "details":"133"}]
"Sources": {"IP":"10.20.10.10", "Subnet Mask": "255.255.255.192"}
}
}
I tried ast.literal_eval, eval
and ended up with an error.
Upvotes: 7
Views: 391
Reputation: 71451
You can use recursion to convert the input to a dictionary, without having to make any adjustments to the string itself beforehand:
import re
s = "Access AR1:\n\tTargets: \n\t\tManagement Name:csw_1\n\t\tObject Name:csw_obj_1\n\t\tdetails:103\n\tSources: \n\t\tIP:10.20.30.40\n\t\tSubnet Mask:255.255.255.255\nAccess AR2:\n\tTargets: \n\t\tManagement Name:csw_2\n\t\tObject Name:csw_obj_2\n\t\tdetails:110\n\tSources: \n\t\tIP:10.20.10.10\n\t\tSubnet Mask:255.255.255.192"
def to_dict(d):
k, v, r = None, [], {}
for *b, a in d:
if not b:
if k is not None:
if not v:
r[j[0]] = (j:=re.split(':\s*', k))[-1]
else:
r[re.split(':\s*', k)[0]] = to_dict(v)
k, v = a, []
else:
v.append([*b[1:], a])
if k is not None:
if not v:
r[j[0]] = (j:=re.split(':\s*', k))[-1]
else:
r[re.split(':\s*', k)[0]] = to_dict(v)
return r
import json
new_s = [re.findall('\t|[^\t]+$', i) for i in s.split('\n')]
print(json.dumps(to_dict(new_s), indent=4))
Output:
{
"Access AR1": {
"Targets": {
"Management Name": "csw_1",
"Object Name": "csw_obj_1",
"details": "103"
},
"Sources": {
"IP": "10.20.30.40",
"Subnet Mask": "255.255.255.255"
}
},
"Access AR2": {
"Targets": {
"Management Name": "csw_2",
"Object Name": "csw_obj_2",
"details": "110"
},
"Sources": {
"IP": "10.20.10.10",
"Subnet Mask": "255.255.255.192"
}
}
}
Upvotes: 1
Reputation: 6826
Your string is nearly yaml - replace the \t with one or more spaces and it loads as YAML.
First:
pip install pyyaml
Then this code works:
import yaml
import pprint
str = "Access AR1:\n\tTargets: \n\t\tManagement Name:csw_1\n\t\tObject Name:csw_obj_1\n\t\tdetails:103\n\tSources: \n\t\tIP:10.20.30.40\n\t\tSubnet Mask:255.255.255.255\nAccess AR2:\n\tTargets: \n\t\tManagement Name:csw_2\n\t\tObject Name:csw_obj_2\n\t\tdetails:110\n\tSources: \n\t\tIP:10.20.10.10\n\t\tSubnet Mask:255.255.255.192"
str1 = str.replace( '\t', ' ' )
res = yaml.load(str1)
pprint.pprint( res )
Output:
{'Access AR1': {'Sources': 'IP:10.20.30.40 Subnet Mask:255.255.255.255',
'Targets': 'Management Name:csw_1 Object Name:csw_obj_1 '
'details:103'},
'Access AR2': {'Sources': 'IP:10.20.10.10 Subnet Mask:255.255.255.192',
'Targets': 'Management Name:csw_2 Object Name:csw_obj_2 '
'details:110'}}
Or, if you really want this as a string then:
str = repr(res)
UPDATE
Just realised that e.g. 'Managent Name:csw_1' isn't being detected as a key:value. Needed a regex re.sub()
to split these onto separate lines to fix this:
import yaml
import pprint
import re
str = "Access AR1:\n\tTargets: \n\t\tManagement Name:csw_1\n\t\tObject Name:csw_obj_1\n\t\tdetails:103\n\tSources: \n\t\tIP:10.20.30.40\n\t\tSubnet Mask:255.255.255.255\nAccess AR2:\n\tTargets: \n\t\tManagement Name:csw_2\n\t\tObject Name:csw_obj_2\n\t\tdetails:110\n\tSources: \n\t\tIP:10.20.10.10\n\t\tSubnet Mask:255.255.255.192"
# replace \t with four-space indent
str1 = str.replace( '\t', ' ' )
# further tweak to split sub-keys like " Management Name:csw_1" onto separate lines
str1 = re.sub(r"^(\s+)(.*?\S:)(\S.*)", r"\1\2\n\1 \3",str1,flags=re.MULTILINE )
res = yaml.load(str1)
pprint.pprint( res )
This is the string after tweaks:
Access AR1:
Targets:
Management Name:
csw_1
Object Name:
csw_obj_1
details:
103
Sources:
IP:
10.20.30.40
Subnet Mask:
255.255.255.255
Access AR2:
Targets:
Management Name:
csw_2
Object Name:
csw_obj_2
details:
110
Sources:
IP:
10.20.10.10
Subnet Mask:
255.255.255.192
Result:
{'Access AR1': {'Sources': {'IP': '10.20.30.40',
'Subnet Mask': '255.255.255.255'},
'Targets': {'Management Name': 'csw_1',
'Object Name': 'csw_obj_1',
'details': 103}},
'Access AR2': {'Sources': {'IP': '10.20.10.10',
'Subnet Mask': '255.255.255.192'},
'Targets': {'Management Name': 'csw_2',
'Object Name': 'csw_obj_2',
'details': 110}}}
Upvotes: 8
Reputation: 368
Hello this is the solution you need
strr = "Access AR1:\n\tTargets: \n\t\tManagement Name:csw_1\n\t\tObject Name:csw_obj_1\n\t\tdetails:103\n\tSources: \n\t\tIP:10.20.30.40\n\t\tSubnet Mask:255.255.255.255\nAccess AR2:\n\tTargets: \n\t\tManagement Name:csw_2\n\t\tObject Name:csw_obj_2\n\t\tdetails:110\n\tSources: \n\t\tIP:10.20.10.10\n\t\tSubnet Mask:255.255.255.192"
nvstr=strr.replace("\n\t\t","-").replace("\n\t","+")
#print(nvstr)
nvdd={}
for u in nvstr.split("\n"):
#print(u)
dts=u.split("+")
nvdd[dts[0]]={}
for el in dts[1:]:
dts1=el.split("-")
nvdd[dts[0]][dts1[0][:-1]]={}
for el1 in dts1[1:]:
k,v=el1.split(":")
nvdd[dts[0]][dts1[0][:-1]][k]=v
print(nvdd)
Upvotes: 2