Read data from an XML file.

Started by mlburgoon71, May 21, 2023, 07:54:18 PM

Previous topic - Next topic


I am trying to write a program to help me with modding some games I play.  I am struggling with figuring out the best way to read in values from the default .xml file, provided by the game, so I can change and manipulate them.  small snippet Example below of the xml file.

But what I want to be able to do is pull the value like so..

eg =  (some command) (/entity_classes/entity_class(@name="zombieNurseFeral")/property(@name="ExperienceGain")

how is this possible to do??  been looking at the wbOmnibus extender and it sounded promising.  but having issues getting it to work. I am hoping I don't have to create my own parser to do this.....

excerpt from the game .xml.
<entity_class name="zombieNurse" extends="zombieTemplateMale">
   <property name="Tags" value="entity,zombie,walker"/>
   <property name="Mesh" value="#Entities/Zombies?Prefabs/ZNurse.prefab"/>
   <property name="SoundRandom" value="Enemies/Base_Zombie_Female/zombiefemaleroam"/>
   <property name="SoundAlert" value="Enemies/Base_Zombie_Female/zombiefemalealert"/>
   <property name="SoundAttack" value="Enemies/Base_Zombie_Female/zombiefemaleattack"/>
   <property name="SoundHurt" value="Enemies/Base_Zombie_Female/zombiefemalepain"/>
   <property name="SoundDeath" value="Enemies/Base_Zombie_Female/zombiefemaledeath"/>
   <property name="SoundSense" value="Enemies/Base_Zombie_Female/zombiefemalesense"/>
   <property name="AvatarController" value="AvatarZombie01Controller"/>
   <property name="ModelType" value="Standard"/>
   <property name="WalkType" value="7"/>
   <property name="Mass" value="130"/>
   <property name="RootMotion" value="true"/>
   <property name="HasDeathAnim" value="true"/>
   <property name="AIPathCostScale" value=".5, .65"/>
   <property name="DanceType" value="2" />

   <!-- Gameplay zombieNurse -->
   <property name="MoveSpeedAggro" value="0.3, 1.35"/> <!-- slim, regular -->
   <property name="HandItem" value="meleeHandZombie01"/>
   <effect_group name="Base Effects">
      <passive_effect name="HealthMax" operation="base_set" value="125"/>

<entity_class name="zombieNurseFeral" extends="zombieNurse">
   <property name="Tags" value="entity,zombie,walker,feral"/>
   <property name="Mesh" value="#Entities/Zombies?Prefabs/ZNurseFeral.prefab"/>

   <!-- Gameplay zombieNurseFeral -->
   <property name="DismemberMultiplierHead" value=".7"/><property name="DismemberMultiplierArms" value=".7"/><property name="DismemberMultiplierLegs" value=".7"/><!-- Feral -->
   <property name="PainResistPerHit" value=".7"/><!-- Feral -->
   <property name="MoveSpeedAggro" value="0.6, 1.45"/> <!-- slim, feral -->
   <property name="JumpMaxDistance" value="4.6, 5"/>
   <property name="HandItem" value="meleeHandZombieFeral"/>
   <property name="ExperienceGain" value="750"/><!-- XP grunt feral -->
   <property name="LootDropProb" value=".03"/> <!-- Feral -->

   <effect_group name="Base Effects">
      <passive_effect name="HealthMax" operation="base_set" value="237"/>
      <!--<passive_effect name="EntityDamage" operation="perc_add" value="0"/>--> <!-- Feral damage -->

<entity_class name="zombieNurseRadiated" extends="zombieNurseFeral">
   <property name="Tags" value="entity,zombie,walker,feral,radiated"/>
   <property name="Mesh" value="#Entities/Zombies?Prefabs/ZNurseRadiated.prefab"/>

   <!-- Gameplay zombieNurseRadiated -->
   <property name="DismemberMultiplierHead" value=".4"/><property name="DismemberMultiplierArms" value=".4"/><property name="DismemberMultiplierLegs" value=".4"/><!-- Radiated -->
   <property name="PainResistPerHit" value=".9"/><!-- Radiated -->
   <property name="ExperienceGain" value="1200"/><!-- XP grunt radiated -->

   <effect_group name="Base Effects">
      <passive_effect name="HealthMax" operation="base_set" value="451"/>
      <!--<passive_effect name="EntityDamage" operation="perc_add" value="0"/>--><!-- Feral damage -->
      <triggered_effect trigger="onOtherDamagedSelf" action="ModifyCVar" target="self" cvar="RadiatedRegenAmount" operation="set" value="4"/>
      <triggered_effect trigger="onOtherDamagedSelf" action="AddBuff" target="self" buff="buffRadiatedRegen"/>


I'm sure Jim would like to hear more specifics about issues you encountered using his Extender. I would think you could write your own parser using the WB CLR and .net assemblies. But you are also interested in changing 'values' [if I read your initial post correctly] which would imply an xml editor of sorts. Thanks for providing the sample .xml - can take a stab at it.

from Tech DB


I will try to take a look soon.  If you are wanting to change your XML, my extender is not a good choice though.  Perhaps the following will help provide a solution.


Code (winbatch) Select


#DefineFunction Load_MSXMLData(xtxt)

  MSXML                    = ObjectOpen("MSXML2.DOMDocument.6.0")
  MSXML.Async              = "False"
  MSXML.preserveWhiteSpace = "False"
  MSXML.validateOnParse    = "False"
  MSXML.resolveExternals   = "False"

  Loaded = MSXML.LoadXML(xtxt)
  If !Loaded Then
    If !IsDefined(stxt) Then stxt = ""
    xPE = MSXML.parseError
    strErrText = "Your XML Document failed to load due the following error.":@CRLF:"Error #: ":xPE.errorCode:": ":xPE.reason:"Line #: ":xPE.Line:@CRLF:"Line Position: ":xPE.linepos:@CRLF:"Position In File: ":xPE.filepos:@CRLF:"Source Text: ":xPE.srcText:@CRLF:"Document URL: ":xPE.url
    xtxt = ""
  End If
  ; Get root of document
  XML_Root = MSXML.documentElement
  Return XML_Root


xtxt = FileGet("xml_test.xml")
XML_Root = Load_MSXMLData(xtxt)
loaded_txt = XML_Root.xml


The .Net System.Xml namespace with WinBatch CLR hosting should be able to perform what the OP requires. The C# examples on the page indicated by the MSFT link below provide a good starting point for a WIL script.

[edit] Jim's COM Automation suggestion is also worth checking out. It can do most/all of what is needed and is likely easier to understand unless you are already familiar with dotNet script in WinBatch. You can dump the progid "MSXML2.DOMDocument.6.0" into The WIL Typeviewer to get some idea of the available properties and methods.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade


Here is some quickie PS code to parse the xml data OP provided with output attached. My thoughts were to quickly collect the properties, queue up any changes then just write back to the xml file with replace functions. Maybe a bit simplistic. Oh, and WB can easily call the PS code. I suspect as Jim suggested that MSXML and xpath could accomplish the same in pure WB.

#parse xml into bar-delimited text for properties
#change xml and txt output as needed
$file = "c:\temp\zombies.txt"
$data = "class|property|value" | out-file $file
[xml] $info = Get-Content -Path C:\temp\zombies.xml
$base = $info.entity_classes.entity_class
ForEach ($class in $base)
     $name = $
     $props = $
     ForEach ($prop in $props)
        $data = $name + "|" +$ + "|" + $prop.value
        $data | Out-File $file -Append


Looks like I missed a step. Attached is output from updated snippet. The XML is more like Json and could be serialized easier if it was. Will try to move the logic into either  WB CLR or MSXML, time permitting.

#parse xml into bar-delimited text for properties
#change xml file/path and txt output as needed
$file = "c:\temp\zombies.txt"
if (Test-Path $file ) {Remove-Item $file}
$data = "class|property|value" | out-file $file
[xml]$info = Get-Content -Path C:\temp\zombies.xml
$base = $info.FirstChild
if ($base.HasChildNodes -eq $true )
   ForEach ($class in $base.ChildNodes)
       $name = $
       $props = $
       $grps = $class.effect_group
       ForEach ($prop in $props)
          $data = $name + "|" +$ + "|" + $prop.value
          $data | Out-File $file -Append
       if ($grps.HasChildNodes -eq $true)
          ForEach ($grp in $grps.ChildNodes)
             $data = $name + "|" +$ + "|" + $grp.value
             $data | Out-File $file -Append