根据属性更新XML节点

时间:2016-10-12 10:15:40

标签: xml vb.net

我有以下用户更改的XML文档:

<?xml version="1.0" encoding="utf-8" ?>
<Cfg xmlns="AddIn" version="161012">
  <SQLConnectionString version="161012">SomeConnectionString</SQLConnectionString>
  <Locale version="161012">
    <Language version="161013">de-DE</Language>
    <LocalSetting version="161012">en-US</LocalSetting>
  </Locale>
</Cfg>

这是最初的文件:

<?xml version="1.0" encoding="utf-8" ?>
<Cfg xmlns="AddIn" version="161012">
  <SQLConnectionString version="161012">SomeConnectionString</SQLConnectionString>
  <Locale version="161012">
    <Language version="161012">en-US</Language>
    <LocalSetting version="161012">en-US</LocalSetting>
  </Locale>
</Cfg>

有些用户将语言更改为&#34; de-DE&#34;。属性&#34;版本&#34;已更新。

用户修改的文档已序列化为以下类:

<Serializable()>
Public Class Cfg

    Private Shared CONFIG_LOCATION As String = GetFolderPath(SpecialFolder.ApplicationData) & "\MyProgram\"
    Private Shared CONFIG_FNAME As String = "Cfg.xml"
    Private Shared CONFIG_FULLPATH As String = CONFIG_LOCATION & CONFIG_FNAME
    Private Shared CONFIG_ASSEMBLY_PATH As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) & "\cfg\"
#Region "Singleton"
    Private Shared ReadOnly _instance As New System.Lazy(Of Cfg)(Function()
                                                                          'Write and read
                                                                          Dim _Cfg As New Cfg
                                                                          If Not File.Exists(CONFIG_FULLPATH) Then
                                                                              'copy xml config file
                                                                              If Not Directory.Exists(CONFIG_LOCATION) Then
                                                                                  Directory.CreateDirectory(CONFIG_LOCATION)
                                                                              End If

                                                                              File.Copy(CONFIG_ASSEMBLY_PATH & CONFIG_FNAME, CONFIG_FULLPATH)
                                                                          Else
                                                                              'This is the point where I need to apply the updates to the xml document

                                                                          End If

                                                                          Dim helper = New XmlSerializerHelper(Of Cfg)()
                                                                          _Cfg = helper.Read(CONFIG_FULLPATH)

                                                                          Return _Cfg

                                                                      End Function, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication)
    Public Shared ReadOnly Property Instance() As Cfg
        Get
            Return _instance.Value
        End Get
    End Property
#End Region

    Private _Locale As Locale
    Private _SQLConnectionString As String
    Public Property Locale() As Locale
        Get
            Return _Locale
        End Get
        Set(value As Locale)
            _Locale = value
        End Set
    End Property

    Public Property SQLConnectionString As String
        Get
            Return _SQLConnectionString
        End Get
        Set(value As String)
            _SQLConnectionString = value
        End Set
    End Property

    Private Sub New()


    End Sub


    Public Function SaveConfigData() As Boolean
        Dim helper = New XmlSerializerHelper(Of Cfg)()
        Dim obj = Me
        helper.Save(CONFIG_FNAME, obj)
        Return True
    End Function

End Class

<Serializable()>
Public Class Locale
    Private _Language As String
    Public Property Language As String
        Get
            Return _Language
        End Get
        Set(value As String)
            _Language = value
        End Set

    End Property

    Private _LocalSetting As String
    Public Property LocalSetting As String
        Get
            Return _LocalSetting
        End Get
        Set(value As String)
            _LocalSetting = value
        End Set
    End Property

    Public Sub New()
    End Sub
End Class

我现在的问题是,如果由于SQL连接字符串已更改而更新源XML文件,我将覆盖该语言的自定义设置。

这是我想要实现的目标:

具有更新的ConnectionString的新配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<Cfg xmlns="AddIn" version="161012">
  <SQLConnectionString version="161013">ThisIsTheNewConnectionString</SQLConnectionString>
  <Locale version="161012">
    <Language version="161012">en-US</Language>
    <LocalSetting version="161012">en-US</LocalSetting>
  </Locale>
</Cfg>

这应该是这样的:

<?xml version="1.0" encoding="utf-8" ?>
<Cfg xmlns="AddIn" version="161012">
  <SQLConnectionString version="161013">ThisIsTheNewConnectionString</SQLConnectionString>
  <Locale version="161012">
    <Language version="161013">de-DE</Language>
    <LocalSetting version="161012">en-US</LocalSetting>
  </Locale>
</Cfg>

我已经尝试过以下方法: how to Update a node in xml? 这实际上有效,但我无法将其实现到我的懒惰类中。 这也是我发现的:How would you compare two XML Documents? 我对所有这些解决方案的主要问题是,我无法在初始化期间结合序列化来管理惰性类。

1 个答案:

答案 0 :(得分:0)

因为我无法在没有帮助的情况下从上面保留XML布局,所以这就是我想出来的,实际上它就像魅力一样!

<Serializable>
<XmlRoot(ElementName:="Cfg", [Namespace]:="YourNSGoesHere")>
Public Class Cfg

    Private Shared CONFIG_LOCATION As String = GetFolderPath(SpecialFolder.ApplicationData) & "\MyProgram\"
    Private Shared CONFIG_FNAME As String = "Cfg.xml"
    Private Shared CONFIG_FULLPATH As String = CONFIG_LOCATION & CONFIG_FNAME
    Private Shared CONFIG_ASSEMBLY_PATH As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) & "\cfg\"
#Region "Singleton"
    Private Shared ReadOnly _instance As New System.Lazy(Of Cfg)(Function()

                                                                          Dim _Cfg As New Cfg
                                                                          Dim helper = New XmlSerializerHelper(Of Cfg)()
                                                                          Dim bDirtyFlag As Boolean
                                                                          'check if config file already exists
                                                                          If Not File.Exists(CONFIG_FULLPATH) Then                                                                              
                                                                              If Not Directory.Exists(CONFIG_LOCATION) Then
                                                                                  Directory.CreateDirectory(CONFIG_LOCATION)
                                                                              End If
                                                                              'if not, serialize standard data
                                                                              helper.Save(CONFIG_FULLPATH, _Cfg)
                                                                          Else
                                                                              'This is the point where I need to apply the updates to the xml document

                                                                          End If

                                                                          'else, read xml file
                                                                              _Cfg = helper.ReadUserConfig(CONFIG_FULLPATH)

                                                                              'example patch routine to update obsolete data in stored user files
                                                                              If _Cfg.SQLConnectionString.Version < DEF_SQLConnectionString.Version Then
                                                                                  _Cfg.SQLConnectionString = DEF_SQLConnectionString
                                                                                  bDirtyFlag = True
                                                                              End If

                                                                              If bDirtyFlag Then
                                                                                  helper.Save(CONFIG_FULLPATH, _Cfg)
                                                                              End If

                                                                          Return _Cfg

                                                                      End Function, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication)
    Public Shared ReadOnly Property Instance() As Cfg
        Get
            Return _instance.Value
        End Get
    End Property
#End Region

    Private _SQLConnectionString As String
    Private _Locale As Locale
    Public Property SQLConnectionString() As PropertyModel(Of String)
        Get
            Return _SQLConnectionString
        End Get
        Set
            _SQLConnectionString = Value
        End Set
    End Property
Public Property Locale() As Locale
    Get
        Return _Locale
    End Get
    Set
        _Locale = Value
    End Set
End Property


#Region "DefaultData"
    Private Shared ReadOnly DEF_Locale = New Locale() With {
    .LocalSetting = New PropertyModel(Of String)() With {
        .Value = "en-US",
        .Version = 1476434998759
    },
    .Language = New PropertyModel(Of String)() With {
        .Value = "en-US",
        .Version = 1476434998759
    }
}
    Private Shared ReadOnly DEF_SQLConnectionString = New PropertyModel(Of String)() With {
    .Value = "SomeConnectionString",
    .Version = 1476434998791 'Timestamp of the creation. If a new value has to be applied by default, just update the timestamp
}
    Private Sub New()
        _SQLConnectionString = DEF_SQLConnectionString
        _Locale = DEF_Locale
    End Sub

#End Region


    Public Function SaveConfigData() As Boolean
        Dim helper = New XmlSerializerHelper(Of Cfg)()
        Dim obj = Me
        helper.Save(CONFIG_FNAME, obj)
        Return True
    End Function

End Class

<Serializable>
Public Class PropertyModel(Of T)
    Private _Version As Long
    Private _Value As T

    <XmlAttribute>
    Public Property Value() As T
        Get
            Return _Value
        End Get
        Set
            _Value = Value
        End Set
    End Property

    <XmlAttribute>
    Public Property Version() As Long
        Get
            Return _Version
        End Get
        Set
            _Version = Value
        End Set
    End Property
End Class

<Serializable>
Public Class Locale
    Private _Language As PropertyModel(Of String)
    Private _LocalSetting As PropertyModel(Of String)

    Public Property Language() As PropertyModel(Of String)
        Get
            Return _Language
        End Get
        Set
            _Language = Value
        End Set
    End Property

    Public Property LocalSetting() As PropertyModel(Of String)
        Get
            Return _LocalSetting
        End Get
        Set
            _LocalSetting = Value
        End Set
    End Property
End Class

'i got this code from SO but i can't remember where, credits go out to creator!
Public Class XmlSerializerHelper(Of T)
    Public _type As Type

    Public Sub New()
        _type = GetType(T)
    End Sub

    Public Sub Save(ByVal SavePath As String, obj As Object)
        Using textWriter As TextWriter = New StreamWriter(SavePath)
            Dim serializer As New XmlSerializer(_type)

            serializer.Serialize(textWriter, obj)
        End Using
    End Sub

    Public Function ReadUserConfig(ByVal LocalXMLPath As String) As T
        Dim result As T

        Using textReader As TextReader = New StreamReader(LocalXMLPath)
            Dim deserializer As New XmlSerializer(_type)

            Try
                result = DirectCast(deserializer.Deserialize(textReader), T)
            Catch ex As Exception
                MsgBox(ex.Message & Chr(13) & ex.StackTrace)
            End Try

        End Using

        Return result
    End Function
End Class

这会产生以下XML输出:

<?xml version="1.0" encoding="utf-8"?>
<Cfg xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="YourNSGoesHere">
  <Locale>
    <Language Value="en-US" Version="1476434998759" />
    <LocalSetting Value="en-US" Version="1476434998759" />
  </Locale>
  <SQLConnectionString Value="SomeConnectionString" Version="1476434998791" />
</Cfg>

我希望这会帮助别人。我会很高兴任何补充或优化。