I'm trying to handle reading and writing of Windows Autounattend.xml files in order to create and modify them. I'm unable to get xmlns:wcm
and xmlns:xsi
attributes be marshaled and unmarshaled correctly.
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>0409:00000409</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DiskConfiguration>
<Disk wcm:action="add">
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
<CreatePartitions>
<!-- Windows RE Tools partition -->
<CreatePartition wcm:action="add">
<Order>1</Order>
<Type>Primary</Type>
<Size>300</Size>
</CreatePartition>
<!-- System partition (ESP) -->
<CreatePartition wcm:action="add">
<Order>2</Order>
<Type>EFI</Type>
<Size>100</Size>
</CreatePartition>
<!-- Microsoft reserved partition (MSR) -->
<CreatePartition wcm:action="add">
<Order>3</Order>
<Type>MSR</Type>
<Size>128</Size>
</CreatePartition>
<!-- Windows partition -->
<CreatePartition wcm:action="add">
<Order>4</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Order>1</Order>
<PartitionID>1</PartitionID>
<Label>WINRE</Label>
<Format>NTFS</Format>
<TypeID>DE94BBA4-06D1-4D40-A16A-BFD50179D6AC</TypeID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>2</Order>
<PartitionID>2</PartitionID>
<Label>System</Label>
<Format>FAT32</Format>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>3</Order>
<PartitionID>3</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>4</Order>
<PartitionID>4</PartitionID>
<Label>Windows</Label>
<Letter>C</Letter>
<Format>NTFS</Format>
</ModifyPartition>
</ModifyPartitions>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>4</PartitionID>
</InstallTo>
<InstallToAvailablePartition>false</InstallToAvailablePartition>
</OSImage>
</ImageInstall>
<UserData>
<ProductKey>
<Key></Key>
<WillShowUI>Never</WillShowUI>
</ProductKey>
<AcceptEula>true</AcceptEula>
<FullName>admin</FullName>
<Organization></Organization>
</UserData>
</component>
</settings>
<settings pass="offlineServicing">
<component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<EnableLUA>false</EnableLUA>
</component>
</settings>
<settings pass="generalize">
<component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipRearm>1</SkipRearm>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputLocale>0409:00000409</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipAutoActivation>true</SkipAutoActivation>
</component>
<component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CEIPEnabled>0</CEIPEnabled>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComputerName>-PC</ComputerName>
<ProductKey></ProductKey>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AutoLogon>
<Password>
<Value></Value>
<PlainText>true</PlainText>
</Password>
<Enabled>true</Enabled>
<Username>admin</Username>
</AutoLogon>
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Home</NetworkLocation>
<SkipUserOOBE>true</SkipUserOOBE>
<SkipMachineOOBE>true</SkipMachineOOBE>
<ProtectYourPC>1</ProtectYourPC>
</OOBE>
<UserAccounts>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value></Value>
<PlainText>true</PlainText>
</Password>
<Description></Description>
<DisplayName>admin</DisplayName>
<Group>Administrators</Group>
<Name>admin</Name>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<RegisteredOrganization></RegisteredOrganization>
<RegisteredOwner>admin</RegisteredOwner>
<DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<Description>Control Panel View</Description>
<Order>1</Order>
<CommandLine>reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ControlPanel" /v StartupPage /t REG_DWORD /d 1 /f</CommandLine>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>2</Order>
<Description>Control Panel Icon Size</Description>
<RequiresUserInput>false</RequiresUserInput>
<CommandLine>reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ControlPanel" /v AllItemsIconView /t REG_DWORD /d 0 /f</CommandLine>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<RequiresUserInput>false</RequiresUserInput>
<CommandLine>cmd /C wmic useraccount where name="admin" set PasswordExpires=false</CommandLine>
<Description>Password Never Expires</Description>
</SynchronousCommand>
</FirstLogonCommands>
<TimeZone>Central Standard Time</TimeZone>
</component>
</settings>
</unattend>
I tried to use xml.Name
,xml.Attr
, and string for this with no luck
const (
// A generic XML header suitable for use with the output of Marshal.
Header = `<?xml version="1.0" encoding="UTF-8"?>` "\n"
WCM = `http://schemas.microsoft.com/WMIConfig/2002/State`
XSI = `http://www.w3.org/2001/XMLSchema-instance`
)
type UserData struct {
AcceptEula bool `xml:"AcceptEula"`
FullName string `xml:"FullName"`
Organization string `xml:"Organization"`
ProductKey struct {
Key string `xml:"Key"`
WillShowUI string `xml:"WillShowUI"`
} `xml:"ProductKey"`
}
type OSImage struct {
XMLName xml.Name `xml:"OSImage"`
InstallToAvailablePartition bool `xml:"InstallToAvailablePartition"`
InstallToDiskID uint32 `xml:"InstallTo>DiskID"`
InstallToPatitionID uint32 `xml:"InstallTo>ParitionID"`
}
type ImageInstall struct {
XMLName xml.Name `xml:"ImageInstall"`
OSImage OSImage `xml:"OSImage"`
}
type CreatePartition struct {
XMLName xml.Name `xml:"CreatePartition"`
Order uint32 `xml:"Order"`
Type string `xml:"Type"`
Size uint64 `xml:"Size,omitempty"`
Extend bool `xml:"Extend,omitempty"`
Action xml.Attr `xml:"action,attr"`
}
type ModifyPartition struct {
XMLName xml.Name `xml:"ModifyPartition"`
Order uint32 `xml:"Order"`
PartitionID uint32 `xml:"PartitionID"`
Label string `xml:"Label,omitempty"`
Format string `xml:"Format,omitempty"`
TypeID string `xml:"TypeID,omitempty"`
Letter string `xml:"Letter,omitempty"`
Action xml.Attr `xml:"action,attr"`
}
type Disk struct {
XMLName xml.Name `xml:"Disk"`
Action xml.Attr `xml:"action,attr"`
DiskID uint32 `xml:"DiskID"`
WillWipeDisk bool `xml:"WillWipeDisk"`
CreatePartitions []*CreatePartition `xml:"CreatePartitions>."`
ModifyPartitions []*ModifyPartition `xml:"ModifyPartitions>."`
}
type DiskConfiguration struct {
XMLName xml.Name `xml:"DiskConfiguration"`
Disks []*Disk `xml:"Disk"`
}
type SetupUILanguage struct {
XMLName xml.Name `xml:"SetupUILanguage"`
UILanguage *UILanguage `xml:"UILanguage"`
}
type UserLocale struct {
XMLName xml.Name `xml:"UserLocale"`
Value string `xml:",chardata"`
}
type InputLocale struct {
XMLName xml.Name `xml:"InputLocale"`
Value string `xml:",chardata"`
}
type SystemLocale struct {
XMLName xml.Name `xml:"SystemLocale"`
Value string `xml:",chardata"`
}
type UILanguage struct {
XMLName xml.Name `xml:"UILanguage"`
Value string `xml:",chardata"`
}
type UILanguageFallback struct {
XMLName xml.Name `xml:"UILanguageFallback"`
Value string `xml:",chardata"`
}
type EnableLUA struct {
XMLName xml.Name `xml:"EnableLUA"`
Value bool `xml:",chardata"`
}
type SkipRearm struct {
XMLName xml.Name `xml:"SkipRearm"`
Value int32 `xml:",chardata"`
}
type SkipAutoActivation struct {
XMLName xml.Name `xml:"SkipAutoActivation"`
Value bool `xml:",chardata"`
}
type CEIPEnabled struct {
XMLName xml.Name `xml:"CEIPEnabled"`
Value int32 `xml:",chardata"`
}
type ComputerName struct {
XMLName xml.Name `xml:"ComputerName"`
Value string `xml:",chardata"`
}
type ProductKey struct {
XMLName xml.Name `xml:"ProductKey"`
Value string `xml:",chardata"`
}
type Password struct {
XMLName xml.Name `xml:"Password"`
Value string `xml:"Value"`
PlainText bool `xml:"PlainText"`
}
type AutoLogon struct {
XMLName xml.Name `xml:"AutoLogon"`
Enabled bool `xml:"Enabled"`
Username string `xml:"Username"`
Password Password `xml:"Password"`
}
type OOBE struct {
XMLName xml.Name `xml:"OOBE"`
HideEULAPage bool `xml:"HideEULAPage,omitempty"`
HideOEMRegistrationScreen bool `xml:"HideOEMRegistrationScreen,omitempty"`
HideOnlineAccountScreens bool `xml:"HideOnlineAccountScreens,omitempty"`
HideWirelessSetupInOOBE bool `xml:"HideWirelessSetupInOOBE,omitempty"`
NetworkLocation string `xml:"NetworkLocation,omitempty"`
SkipUserOOBE bool `xml:"SkipUserOOBE,omitempty"`
SkipMachineOOBE bool `xml:"SkipMachineOOBE,omitempty"`
ProtectYourPC int32 `xml:"ProtectYourPC,omitempty"`
}
type LocalAccount struct {
XMLName xml.Name `xml:"LocalAccount"`
Action xml.Attr `xml:"action,attr"`
Password Password `xml:"Password"`
Description string `xml:"Description"`
DisplayName string `xml:"DisplayName"`
Group string `xml:"Group"`
Name string `xml:"Name"`
}
type LocalAccounts struct {
XMLName xml.Name
LocalAccount []*LocalAccount `xml:"LocalAccount"`
}
type UserAccounts struct {
XMLName xml.Name `xml:"UserAccounts"`
LocalAccounts LocalAccounts `xml:"LocalAccounts"`
}
type RegisteredOrganization struct {
XMLName xml.Name `xml:"RegisteredOrganization"`
Value string `xml:",chardata"`
}
type RegisteredOwner struct {
XMLName xml.Name `xml:"RegisteredOwner"`
Value string `xml:",chardata"`
}
type DisableAutoDaylightTimeSet struct {
XMLName xml.Name `xml:"DisableAutoDaylightTimeSet"`
Value bool `xml:",chardata"`
}
type CommandLine struct {
XMLName xml.Name `xml:"CommandLine"`
Value string `xml:",innerxml"`
}
type SynchronousCommand struct {
XMLName xml.Name `xml:"SynchronousCommand"`
Order uint32 `xml:"Order"`
Description string `xml:"Description"`
RequiresUserInput bool `xml:"RequiresUserInput"`
CommandLine CommandLine `xml:"CommandLine"`
}
type FirstLogonCommands struct {
XMLName xml.Name `xml:"FirstLogonCommands"`
SynchronousCommand []*SynchronousCommand `xml:"SynchronousCommand"`
}
type TimeZone struct {
XMLName xml.Name `xml:"TimeZone"`
Value string `xml:",chardata"`
}
type Component struct {
XMLName xml.Name `xml:"component"`
Name string `xml:"name,attr"`
ProcessorArchitecture string `xml:"processorArchitecture,attr"`
PublicKeyToken string `xml:"publicKeyToken,attr"`
Language string `xml:"language,attr"`
VersionScope string `xml:"versionScope,attr"`
// optional compontents
SetupUILanguage *SetupUILanguage `xml:"SetupUILanguage"`
UserLocale *UserLocale `xml:"UserLocale"`
InputLocale *InputLocale `xml:"InputLocale"`
SystemLocale *SystemLocale `xml:"SystemLocale"`
UILanguage *UILanguage `xml:"UILanguage"`
UILanguageFallback *UILanguageFallback `xml:"UILanguageFallback"`
DiskConfiguration *DiskConfiguration `xml:"DiskConfiguration,omitempty"`
ImageInstall *ImageInstall `xml:"ImageInstall,omitempty"`
UserData *UserData `xml:"UserData,omitempty"`
EnableLUA *EnableLUA `xml:"EnableLUA,omitempty"`
SkipRearm *SkipRearm `xml:"SkipRearm,omitempty"`
ProductKey *ProductKey `xml:"ProductKey,omitempty"`
ComputerName *ComputerName `xml:"ComputerName,omitempty"`
SkipAutoActivation *SkipAutoActivation `xml:"SkipAutoActivation,omitempty"`
CEIPEnabled *CEIPEnabled `xml:"CEIPEnabled,omitempty"`
AutoLogon *AutoLogon `xml:"AutoLogon,omitempty"`
OOBE *OOBE `xml:"OOBE,omitempty"`
UserAccounts *UserAccounts `xml:"UserAccounts"`
RegisteredOrganization *RegisteredOrganization `xml:"RegisteredOrganization,omitempty"`
RegisteredOwner *RegisteredOwner `xml:"RegisteredOwner,omitempty"`
DisableAutoDaylightTimeSet *DisableAutoDaylightTimeSet `xml:"DisableAutoDaylightTimeSet,omitempty"`
FirstLogonCommands *FirstLogonCommands `xml:"FirstLogonCommands,omitempty"`
TimeZone *TimeZone `xml:"TimeZone,omitempty"`
}
type Settings struct {
XMLName xml.Name `xml:"settings"`
Pass string `xml:"pass,attr"`
Components []*Component `xml:"component"`
}
type Unattend struct {
XMLName xml.Name `xml:"unattend"`
XMLNS xml.Attr `xml:"xmlns,attr"`
Settings []*Settings `xml:"settings"`
}
CodePudding user response:
xmlns:wcm
and xmlns:xsi
are not the real attributes, and they are processed in a special way. Your XML has neither elements nor attributes from wcm
and xsi
schemas, and they are dropped. Why do you need them in your code?
Still If you really-really need them, you can add the following field to the Component
structure:
Attr []xml.Attr `xml:",any,attr"`
xml.Unmarshal
documentation says:
If the XML element has an attribute not handled by the previous rule and the struct has a field with an associated tag containing ",any,attr", Unmarshal records the attribute value in the first such field.
Here is an example: https://go.dev/play/p/tGDh5Ay1kZW
func main() {
var u Unattend
err := xml.Unmarshal([]byte(document), &u)
if err != nil {
log.Fatal(err)
}
for _, a := range u.Settings[0].Components[0].Attr {
fmt.Printf("Unmatched attributes: %s:%s = %s\n", a.Name.Space, a.Name.Local, a.Value)
}
}
Output:
Unmatched attributes: xmlns:wcm = http://schemas.microsoft.com/WMIConfig/2002/State
Unmatched attributes: xmlns:xsi = http://www.w3.org/2001/XMLSchema-instance
Note 1. What is not so good with this solution: it handles extra attributes in <component/>
only. But xmlns:XXX
attributes can appeare in any element. In general case you should add Attr
array in all structures of your data model.
Note 2. The attributes xmlns:wcm
and xmlns:xsi
bear no business logic. They give no information regarding settings and components. Do you really need them?
Note 3. There is absolutely no requirement to name these attributes xmlns:wcm
and xmlns:xsi
. It is just a common practice. XML document can name them xmlns:foo
and xmlns:bar
- it is perfectly valid. Even the values "http://schemas.microsoft.com/WMIConfig/2002/State"
and "http://www.w3.org/2001/XMLSchema-instance"
have no meaning. They could be any string, the only requirement is to match URI format. E.g. urn:microsoft:wmi-config:2002:state
is as valid as http://schemas.microsoft.com/WMIConfig/2002/State
Parsing those attributes in Component
is very specific and could fail on olther valid XML documents that declare those attributes in other elements or with other suffixes or values.
UPDATE
Marshalling works not as expected. xml.Marshal
handles xmlns
namespace specifically and converts Attr
for xmlns:wcm
and xmlns:xsi
into the namespace _xmlns
<component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:_xmlns="xmlns" _xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" _xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
</component>
The workaround is to use a special type for the attributes:
type XMLNSAttr struct {
Name xml.Name
Value string
}
func (a XMLNSAttr) Label() string {
if a.Name.Space == "" {
return a.Name.Local
}
if a.Name.Local == "" {
return a.Name.Space
}
return a.Name.Space ":" a.Name.Local
}
func (a *XMLNSAttr) UnmarshalXMLAttr(attr xml.Attr) error {
a.Name = attr.Name
a.Value = attr.Value
return nil
}
func (a XMLNSAttr) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
return xml.Attr{
Name: xml.Name{
Space: "",
Local: a.Label(),
},
Value: a.Value,
}, nil
}
This type does the trick by constructing an attribute with the namespace embedded into the local name. The field
Attr []xml.Attr `xml:",any,attr"`
correctly decodes/encodes xmlns:xxx
attributes
Example: https://go.dev/play/p/VDofREl5nf1
Output:
Unmatched attributes: xmlns:wcm = http://schemas.microsoft.com/WMIConfig/2002/State
Unmatched attributes: xmlns:xsi = http://www.w3.org/2001/XMLSchema-instance
<unattend>
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
</component>
...
</settings>
</unattend>