Skip to content

Instantly share code, notes, and snippets.

@stdray
Created July 10, 2015 12:59
Show Gist options
  • Select an option

  • Save stdray/a667c4ba97ff3f6d3d82 to your computer and use it in GitHub Desktop.

Select an option

Save stdray/a667c4ba97ff3f6d3d82 to your computer and use it in GitHub Desktop.
using Nemerle;
using System;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Interfax.SM.ETL.Mapping.Macros.DSL;
using Interfax.SM.ETL.Mapping.Types;
using Interfax.SM.Utils;
namespace Interfax.SM.ETL.Mapping.Goszakupki.Goszakupki44
{
///Модуль, который десериализует объекты в зависимости от версии схемы
public module DispatchDeserializer
{
///По версии схемы возвращает тип рутового элемента и соответствующий десериализатор
Dispatch(version : string) : Type * IDeserializer
{
| "5.0" | "5.1" | "5.2" | null =>
( typeof(Goszakupki44.Actual.Zakupki.Gov.Ru.Oos.Export.V1.export),
Goszakupki44.Actual.Deserializer.Instance )
| "1.0" | "4.1" | "4.2" | "4.3" | "4.3.100" | "4.4" | "4.4.2" | "4.5" | "4.6" =>
( typeof(Goszakupki44.Version_4_6.Zakupki.Gov.Ru.Oos.Export.V1.export),
Goszakupki44.Version_4_6.Deserializer.Instance )
}
//Десериализует рутовый объект документа в зависимости от версии схемы
public RootObject(version : string, xml : XDocument) : object
{
def (rootType, _) = Dispatch(version);
//очищаем и десериализуем
Clean(version, xml).DeserializeFromXml(rootType)
}
//Десериализует объект документа в зависимости о версии схемы и типа документа
public DocumentObject(version : string, documentType : string, xml : string) : object
{
def (_, deserializer) = Dispatch(version);
deserializer.Deserialize(documentType, xml);
}
//Выполняет очистку документа от лишних тегов и исправляет известные ошибки в XML
Clean(version : string, xml : XDocument) : XDocument
{
def praparedContent = XDocument.Load(xml.CreateReader());
//битые пространства имен, видимо какие-то криво сериализованные документ
def fakeNamespaces =
[
@"http://zakupki.gov.ru/oos/printform/1",
@"http://zakupki.gov.ru/oos/integration/1"
];
//реализация очистки
def clean(el)
{
def name = el.Name.LocalName;
def parentName = el.Parent?.Name?.LocalName;
def changes = System.Collections.Generic.List();
mutable nsChanged = false;
//фиксим кривые префиксы неймспейсов
foreach(attr in el.Attributes())
when(fakeNamespaces.Contains(attr.Value))
{
changes.Add(() =>
{
el.SetAttributeValue(attr.Name, @"http://zakupki.gov.ru/oos/types/1");
nsChanged = true;
});
}
//если простая чистка не помогла, пробуем перестроить тег
when(!nsChanged && fakeNamespaces.Contains(el.Name.NamespaceName))
{
def replaceTag()
{
def preparedElBoby =
if(el.HasElements) //если есть дочерние элементы
el.Elements() //то собираем их рекурсивно, применя очистку
.Select(e => { def (_, r) = clean(e); r} )
.ToList()
else
el.Value : object; //иначе там какой-то текст
def preparedEl = XElement(el.Name.LocalName, preparedElBoby); //новый элемент без неймспейса
preparedEl.ReplaceAttributes(el.Attributes()); //добавляем ему атрибуты
when(el.Parent != null) //если исходные элемент в дереве
el.ReplaceWith(preparedEl); //заменяем его
}
changes.Add(replaceTag);
}
//чистим
when( (String.IsNullOrWhiteSpace(el.Value) && !el.Attributes().Any()) //пустые теги (нет значения, нет атрибутов)
|| (name == "signature" && parentName == "printForm") //цифровую подпись печатной формы
|| name == "cryptoSigns" //цифровую подпись документа
|| name == "fcsClarification") //какую-то ненужность, которая много занимает
{
changes.Add(el.Remove);
}
//фиксим значения представленые в экспоненциальной форме (XmlSerializer не умеет с ними работать)
when(name == "value" && parentName == "quantity")
safe el.Value = decimal.Parse(el.Value, NumberStyles.Any).ToString();
//фиксим кривой уровень бюджета
when(name == "budgetLevel" && parentName == "finances")
{
el.Value=
if(el.Value.Length > 2)
el.Value.Substring(0, 2);
else if(version == "5.0" && el.Value[0] == '0')
"1" + el.Value.Substring(1)
else
el.Value;
}
(changes, el);
}
try
{
def changes = praparedContent
.Descendants() //идем по дереву элементов
.Select(clean) //для каждого вызываем очистку
.SelectMany((changes, _) => changes) //собираем их в единый список
.Reverse() //инвертируем, потому что изменение элемента затрагивают его потом и последующие элементы
.ToList();
//за один проход накатываем изменения
foreach(change in changes)
change();
praparedContent;
}
catch
{
| ex =>
System.Diagnostics.Trace.WriteLine(ex.Message);
throw;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment